From a626f3c90d5194bdc629d2244a291170e9338fbe Mon Sep 17 00:00:00 2001 From: Prateek Surana Date: Fri, 24 May 2024 16:10:26 +0530 Subject: [PATCH 01/45] feat: Changes for multitenancy dashboard (#209) * feat: Implement method to get plugin config properties (#186) * Add a new method to get the config as json * Update changelog * Remove the check for protected config from method * Revert version changes * Update descriptions * Update config properties descriptions to match those in the yaml files * Throw error when invalid fields * Refactor getConfigFieldsInfo method in Start and PostgreSQLConfig classes * fix: config info * fix: update config * fix: cleanup * fix: cleanup * fix: refactor config description * fix: type fix * fix: pr comments * fix: pr comments * fix: PR comments * fix: version and changelog * fix: remove unnecessary fields for dashboard * fix: refactor dashboard related * fix: impl --------- Co-authored-by: Sattvik Chakravarthy --- CHANGELOG.md | 15 ++ build.gradle | 2 +- config.yaml | 6 +- devConfig.yaml | 6 +- pluginInterfaceSupported.json | 2 +- .../supertokens/storage/postgresql/Start.java | 9 +- .../postgresql/annotations/DashboardInfo.java | 35 ++++ .../postgresql/config/PostgreSQLConfig.java | 151 ++++++++++++---- .../queries/MultitenancyQueries.java | 12 +- .../multitenancy/TenantConfigSQLHelper.java | 26 +-- .../storage/postgresql/test/LoggingTest.java | 1 - .../test/PostgresSQLConfigTest.java | 164 ++++++++++++++++++ .../test/multitenancy/StorageLayerTest.java | 1 - 13 files changed, 375 insertions(+), 55 deletions(-) create mode 100644 src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java create mode 100644 src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a99afbf0..518be7b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [7.1.0] + +- Adds implementation for a new method `getConfigFieldsInfo` to fetch the plugin config fields. +- Adds `null` state for `firstFactors` and `providers` by adding `is_first_factors_null` and `is_third_party_providers_null` fields in `tenant_configs` table + +### Migration + +```sql +ALTER TABLE tenant_configs ADD COLUMN IF NOT EXISTS is_first_factors_null BOOLEAN DEFAULT TRUE; +ALTER TABLE tenant_configs ADD COLUMN IF NOT EXISTS is_third_party_providers_null BOOLEAN DEFAULT TRUE; + +ALTER TABLE tenant_configs ALTER COLUMN is_first_factors_null DROP DEFAULT; +ALTER TABLE tenant_configs ALTER COLUMN is_third_party_providers_null DROP DEFAULT; +``` + ## [7.0.1] - 2024-04-17 - Fixes issues with partial failures during tenant creation diff --git a/build.gradle b/build.gradle index cbd69075..fdcd09f6 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "7.0.1" +version = "7.1.0" repositories { mavenCentral() diff --git a/config.yaml b/config.yaml index 38ade78f..f42b0851 100644 --- a/config.yaml +++ b/config.yaml @@ -22,7 +22,7 @@ postgresql_config_version: 0 # (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. The PostgreSQL user to use to query the database. # If the relevant tables are not already created by you, this user should have the -# ability to create new tables. To see the tables needed, visit: https://supertokens.io/docs/community/getting-started/database-setup/postgresql +# ability to create new tables. To see the tables needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database-setup/postgresql # postgresql_user: # (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. Password for the PostgreSQL user. If you have not set a password @@ -37,7 +37,7 @@ postgresql_config_version: 0 # postgresql_table_schema: # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "") string value. A prefix to add to all table names managed by -# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined +# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined. # postgresql_table_names_prefix: # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "key_value") string value. Specify the name of the table that will @@ -66,7 +66,7 @@ postgresql_config_version: 0 # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "thirdparty_users") string value. Specify the name of the table # that will store the thirdparty recipe users. -# postgresql_thirdparty_users_table_name +# postgresql_thirdparty_users_table_name: # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: 60000) long value. Timeout in milliseconds for the idle connections # to be closed. diff --git a/devConfig.yaml b/devConfig.yaml index a25dba97..09eb3e55 100644 --- a/devConfig.yaml +++ b/devConfig.yaml @@ -23,7 +23,7 @@ postgresql_config_version: 0 # (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. The PostgreSQL user to use to query the database. # If the relevant tables are not already created by you, this user should have the -# ability to create new tables. To see the tables needed, visit: TODO +# ability to create new tables. To see the tables needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database-setup/postgresql postgresql_user: "root" # (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. Password for the PostgreSQL instance. If you do not have a @@ -39,7 +39,7 @@ postgresql_password: "root" # postgresql_table_schema: # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "") string value. A prefix to add to all table names managed by -# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined +# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined. # postgresql_table_names_prefix: # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "key_value") string value. Specify the name of the table that will @@ -68,7 +68,7 @@ postgresql_password: "root" # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "thirdparty_users") string value. Specify the name of the table # that will store the thirdparty recipe users. -# postgresql_thirdparty_users_table_name +# postgresql_thirdparty_users_table_name: # (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: 60000) long value. Timeout in milliseconds for the idle connections # to be closed. diff --git a/pluginInterfaceSupported.json b/pluginInterfaceSupported.json index 476e2b85..0dedee88 100644 --- a/pluginInterfaceSupported.json +++ b/pluginInterfaceSupported.json @@ -1,6 +1,6 @@ { "_comment": "contains a list of plugin interfaces branch names that this core supports", "versions": [ - "6.1" + "6.2" ] } \ No newline at end of file diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 82832bb5..0ee1839f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -113,7 +113,9 @@ public class Start private static String[] PROTECTED_DB_CONFIG = new String[]{"postgresql_connection_pool_size", "postgresql_connection_uri", "postgresql_host", "postgresql_port", "postgresql_user", "postgresql_password", "postgresql_database_name", "postgresql_table_schema", "postgresql_idle_connection_timeout", - "postgresql_minimum_idle_connections"}; + "postgresql_minimum_idle_connections", "postgresql_connection_attributes", "postgresql_connection_scheme", + "postgresql_table_names_prefix" + }; private static final Object appenderLock = new Object(); public static boolean silent = false; private ResourceDistributor resourceDistributor = new ResourceDistributor(); @@ -2830,6 +2832,11 @@ public Set getValidFieldsInConfig() { return PostgreSQLConfig.getValidFields(); } + @Override + public List getPluginConfigFieldsInfo() { + return PostgreSQLConfig.getConfigFieldsInfoForDashboard(this); + } + @Override public void setLogLevels(Set logLevels) { Config.setLogLevels(this, logLevels); diff --git a/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java b/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java new file mode 100644 index 00000000..a40b0a1e --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java @@ -0,0 +1,35 @@ +/* + * 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to provide a description for a configuration fields. To be used on the fields of `CoreConfig` and config + * class in the plugin like `PostgreSQLConfig`, `MysqlConfig`, etc. + */ +@Retention(RetentionPolicy.RUNTIME) // Make annotation accessible at runtime so that config descriptions can be read from API +@Target(ElementType.FIELD) // Annotation can only be applied to fields +public @interface DashboardInfo { + String description() default ""; + boolean isOptional() default false; + String defaultValue() default ""; + boolean isEditable() default false; +} 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 e0a0c682..7ff9daa1 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -19,17 +19,19 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; + +import io.supertokens.pluginInterface.ConfigFieldInfo; import io.supertokens.pluginInterface.exceptions.InvalidConfigException; -import io.supertokens.storage.postgresql.annotations.ConnectionPoolProperty; -import io.supertokens.storage.postgresql.annotations.IgnoreForAnnotationCheck; -import io.supertokens.storage.postgresql.annotations.NotConflictingWithinUserPool; -import io.supertokens.storage.postgresql.annotations.UserPoolProperty; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.annotations.*; import java.lang.reflect.Field; import java.net.URI; +import java.util.ArrayList; import java.util.HashSet; import java.util.Map; import java.util.Objects; @@ -44,80 +46,100 @@ public class PostgreSQLConfig { @JsonProperty @ConnectionPoolProperty + @DashboardInfo(description = "Defines the connection pool size to PostgreSQL. Please see https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing", defaultValue = "10", isOptional = true, isEditable = true) private int postgresql_connection_pool_size = 10; @JsonProperty @UserPoolProperty + @DashboardInfo(description = "Specify the postgresql host url here. For example: - \"localhost\" - \"192.168.0.1\" - \"\" - \"example.com\"", defaultValue = "\"localhost\"", isOptional = true) private String postgresql_host = null; @JsonProperty @UserPoolProperty + @DashboardInfo(description = "Specify the port to use when connecting to PostgreSQL instance.", defaultValue = "5432", isOptional = true) private int postgresql_port = -1; @JsonProperty @ConnectionPoolProperty + @DashboardInfo(description = "The PostgreSQL user to use to query the database. If the relevant tables are not already created by you, this user should have the ability to create new tables. To see the tables needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database-setup/postgresql", defaultValue = "null") private String postgresql_user = null; @JsonProperty @ConnectionPoolProperty + @DashboardInfo(description = "Password for the PostgreSQL user. If you have not set a password make this an empty string.", defaultValue = "null") private String postgresql_password = null; @JsonProperty @UserPoolProperty + @DashboardInfo(description = "The database name to store SuperTokens related data.", defaultValue = "\"supertokens\"", isOptional = true) private String postgresql_database_name = null; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "A prefix to add to all table names managed by SuperTokens. An \"_\" will be added between this prefix and the actual table name if the prefix is defined.", defaultValue = "\"\"", isOptional = true) private String postgresql_table_names_prefix = ""; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "Specify the name of the table that will store secret keys and app info necessary for the functioning sessions.", defaultValue = "\"key_value\"", isOptional = true) private String postgresql_key_value_table_name = null; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "Specify the name of the table that will store the session info for users.", defaultValue = "\"session_info\"", isOptional = true) private String postgresql_session_info_table_name = null; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "Specify the name of the table that will store the user information, along with their email and hashed password.", defaultValue = "\"emailpassword_users\"", isOptional = true) private String postgresql_emailpassword_users_table_name = null; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "Specify the name of the table that will store the password reset tokens for users.", defaultValue = "\"emailpassword_pswd_reset_tokens\"", isOptional = true) private String postgresql_emailpassword_pswd_reset_tokens_table_name = null; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "Specify the name of the table that will store the email verification tokens for users.", defaultValue = "\"emailverification_tokens\"", isOptional = true) private String postgresql_emailverification_tokens_table_name = null; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "Specify the name of the table that will store the verified email addresses.", defaultValue = "\"emailverification_verified_emails\"", isOptional = true) private String postgresql_emailverification_verified_emails_table_name = null; @JsonProperty @NotConflictingWithinUserPool + @DashboardInfo(description = "Specify the name of the table that will store the thirdparty recipe users.", defaultValue = "\"thirdparty_users\"", isOptional = true) private String postgresql_thirdparty_users_table_name = null; @JsonProperty @UserPoolProperty + @DashboardInfo(description = "The schema for tables.", defaultValue = "\"public\"", isOptional = true) private String postgresql_table_schema = "public"; @JsonProperty @IgnoreForAnnotationCheck + @DashboardInfo(description = "Specify the PostgreSQL connection URI in the following format: postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2... Values provided via other configs will override values provided by this config.", defaultValue = "null", isOptional = true) private String postgresql_connection_uri = null; @ConnectionPoolProperty + @DashboardInfo(description = "The connection attributes of the PostgreSQL database.", defaultValue = "\"allowPublicKeyRetrieval=true\"", isOptional = true) private String postgresql_connection_attributes = "allowPublicKeyRetrieval=true"; @ConnectionPoolProperty + @DashboardInfo(description = "The scheme of the PostgreSQL database.", defaultValue = "\"postgresql\"", isOptional = true) private String postgresql_connection_scheme = "postgresql"; @JsonProperty @ConnectionPoolProperty + @DashboardInfo(description = "Timeout in milliseconds for the idle connections to be closed.", defaultValue = "60000", isOptional = true, isEditable = true) private long postgresql_idle_connection_timeout = 60000; @JsonProperty @ConnectionPoolProperty + @DashboardInfo(description = "Minimum number of idle connections to be kept active. If not set, minimum idle connections will be same as the connection pool size.", defaultValue = "null", isOptional = true, isEditable = true) private Integer postgresql_minimum_idle_connections = null; @IgnoreForAnnotationCheck @@ -134,6 +156,69 @@ public static Set getValidFields() { return validFields; } + public static ArrayList getConfigFieldsInfoForDashboard(Start start) { + ArrayList result = new ArrayList(); + + JsonObject tenantConfig = new Gson().toJsonTree(Config.getConfig(start)).getAsJsonObject(); + + PostgreSQLConfig defaultConfigObj = new PostgreSQLConfig(); + try { + defaultConfigObj.validateAndNormalise(true); // skip validation and just populate defaults + } catch (InvalidConfigException e) { + throw new IllegalStateException(e); // should never happen + } + + JsonObject defaultConfig = new Gson().toJsonTree(defaultConfigObj).getAsJsonObject(); + + for (String fieldId : PostgreSQLConfig.getValidFields()) { + try { + Field field = PostgreSQLConfig.class.getDeclaredField(fieldId); + if (!field.isAnnotationPresent(DashboardInfo.class)) { + continue; + } + + if (field.getName().endsWith("_table_name")) { + continue; // do not show + } + + String key = field.getName(); + String description = field.isAnnotationPresent(DashboardInfo.class) + ? field.getAnnotation(DashboardInfo.class).description() + : ""; + boolean isDifferentAcrossTenants = true; + + String valueType = null; + + Class fieldType = field.getType(); + + if (fieldType == String.class) { + valueType = "string"; + } else if (fieldType == boolean.class) { + valueType = "boolean"; + } else if (fieldType == int.class || fieldType == long.class || fieldType == Integer.class) { + valueType = "number"; + } else { + throw new RuntimeException("Unknown field type " + fieldType.getName()); + } + + JsonElement value = tenantConfig.get(field.getName()); + + JsonElement defaultValue = defaultConfig.get(field.getName()); + boolean isNullable = defaultValue == null; + + boolean isEditable = field.getAnnotation(DashboardInfo.class).isEditable(); + + result.add(new ConfigFieldInfo( + key, valueType, value, description, isDifferentAcrossTenants, + null, isNullable, defaultValue, true, isEditable)); + + } catch (NoSuchFieldException e) { + continue; + } + } + return result; + } + public String getTableSchema() { return postgresql_table_schema; } @@ -339,41 +424,47 @@ private String addSchemaToTableName(String tableName) { } public void validateAndNormalise() throws InvalidConfigException { + validateAndNormalise(false); + } + + private void validateAndNormalise(boolean skipValidation) throws InvalidConfigException { if (isValidAndNormalised) { return; } - if (postgresql_connection_uri != null) { - try { - URI ignored = URI.create(postgresql_connection_uri); - } catch (Exception e) { - throw new InvalidConfigException( - "The provided postgresql connection URI has an incorrect format. Please use a format like " - + "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2..."); - } - } else { - if (this.getUser() == null) { - throw new InvalidConfigException( - "'postgresql_user' and 'postgresql_connection_uri' are not set. Please set at least one of " - + "these values"); + if (!skipValidation) { + if (postgresql_connection_uri != null) { + try { + URI ignored = URI.create(postgresql_connection_uri); + } catch (Exception e) { + throw new InvalidConfigException( + "The provided postgresql connection URI has an incorrect format. Please use a format like " + + "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2..."); + } + } else { + if (this.getUser() == null) { + throw new InvalidConfigException( + "'postgresql_user' and 'postgresql_connection_uri' are not set. Please set at least one of " + + "these values"); + } } - } - - if (postgresql_connection_pool_size <= 0) { - throw new InvalidConfigException( - "'postgresql_connection_pool_size' in the config.yaml file must be > 0"); - } - if (postgresql_minimum_idle_connections != null) { - if (postgresql_minimum_idle_connections < 0) { + if (postgresql_connection_pool_size <= 0) { throw new InvalidConfigException( - "'postgresql_minimum_idle_connections' must be >= 0"); + "'postgresql_connection_pool_size' in the config.yaml file must be > 0"); } - if (postgresql_minimum_idle_connections > postgresql_connection_pool_size) { - throw new InvalidConfigException( - "'postgresql_minimum_idle_connections' must be less than or equal to " - + "'postgresql_connection_pool_size'"); + if (postgresql_minimum_idle_connections != null) { + if (postgresql_minimum_idle_connections < 0) { + throw new InvalidConfigException( + "'postgresql_minimum_idle_connections' must be >= 0"); + } + + if (postgresql_minimum_idle_connections > postgresql_connection_pool_size) { + throw new InvalidConfigException( + "'postgresql_minimum_idle_connections' must be less than or equal to " + + "'postgresql_connection_pool_size'"); + } } } 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 0d9bf826..447ffb40 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java @@ -51,6 +51,8 @@ static String getQueryToCreateTenantConfigsTable(Start start) { + "email_password_enabled BOOLEAN," + "passwordless_enabled BOOLEAN," + "third_party_enabled BOOLEAN," + + "is_first_factors_null BOOLEAN," + + "is_third_party_providers_null BOOLEAN," + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)" + ");"; // @formatter:on @@ -173,11 +175,13 @@ private static void executeCreateTenantQueries(Start start, Connection sqlCon, T TenantConfigSQLHelper.create(start, sqlCon, tenantConfig); - for (ThirdPartyConfig.Provider provider : tenantConfig.thirdPartyConfig.providers) { - ThirdPartyProviderSQLHelper.create(start, sqlCon, tenantConfig, provider); + if (tenantConfig.thirdPartyConfig.providers != null) { + for (ThirdPartyConfig.Provider provider : tenantConfig.thirdPartyConfig.providers) { + ThirdPartyProviderSQLHelper.create(start, sqlCon, tenantConfig, provider); - for (ThirdPartyConfig.ProviderClient providerClient : provider.clients) { - ThirdPartyProviderClientSQLHelper.create(start, sqlCon, tenantConfig, provider, providerClient); + for (ThirdPartyConfig.ProviderClient providerClient : provider.clients) { + ThirdPartyProviderClientSQLHelper.create(start, sqlCon, tenantConfig, provider, providerClient); + } } } diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java index 1a2e00b2..3c9c100a 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java @@ -16,15 +16,11 @@ package io.supertokens.storage.postgresql.queries.multitenancy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; import io.supertokens.pluginInterface.RowMapper; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.*; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.queries.utils.JsonUtils; -import io.supertokens.storage.postgresql.utils.Utils; import java.sql.Connection; import java.sql.ResultSet; @@ -56,12 +52,16 @@ public static TenantConfigSQLHelper.TenantConfigRowMapper getInstance(ThirdParty @Override public TenantConfig map(ResultSet result) throws StorageQueryException { try { + boolean isFirstFactorsNull = result.getBoolean("is_first_factors_null"); + boolean isThirdPartyProvidersNull = result.getBoolean("is_third_party_providers_null"); return new TenantConfig( new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")), new EmailPasswordConfig(result.getBoolean("email_password_enabled")), - new ThirdPartyConfig(result.getBoolean("third_party_enabled"), this.providers), + new ThirdPartyConfig( + result.getBoolean("third_party_enabled"), + providers.length == 0 && isThirdPartyProvidersNull ? null : providers), new PasswordlessConfig(result.getBoolean("passwordless_enabled")), - firstFactors.length == 0 ? null : firstFactors, + firstFactors.length == 0 && isFirstFactorsNull ? null : firstFactors, requiredSecondaryFactors.length == 0 ? null : requiredSecondaryFactors, JsonUtils.stringToJsonObject(result.getString("core_config")) ); @@ -74,16 +74,19 @@ public TenantConfig map(ResultSet result) throws StorageQueryException { public static TenantConfig[] selectAll(Start start, HashMap> providerMap, HashMap firstFactorsMap, HashMap requiredSecondaryFactorsMap) throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, core_config," - + " email_password_enabled, passwordless_enabled, third_party_enabled FROM " + + " email_password_enabled, passwordless_enabled, third_party_enabled, " + + " is_first_factors_null, is_third_party_providers_null FROM " + getConfig(start).getTenantConfigsTable() + ";"; TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> {}, result -> { List temp = new ArrayList<>(); while (result.next()) { TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); - ThirdPartyConfig.Provider[] providers = null; + ThirdPartyConfig.Provider[] providers; if (providerMap.containsKey(tenantIdentifier)) { providers = providerMap.get(tenantIdentifier).values().toArray(new ThirdPartyConfig.Provider[0]); + } else { + providers = new ThirdPartyConfig.Provider[0]; } String[] firstFactors = firstFactorsMap.containsKey(tenantIdentifier) ? firstFactorsMap.get(tenantIdentifier) : new String[0]; @@ -104,8 +107,9 @@ public static void create(Start start, Connection sqlCon, TenantConfig tenantCon throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getTenantConfigsTable() + "(connection_uri_domain, app_id, tenant_id, core_config," - + " email_password_enabled, passwordless_enabled, third_party_enabled)" - + " VALUES(?, ?, ?, ?, ?, ?, ?)"; + + " email_password_enabled, passwordless_enabled, third_party_enabled," + + " is_first_factors_null, is_third_party_providers_null)" + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantConfig.tenantIdentifier.getConnectionUriDomain()); @@ -115,6 +119,8 @@ public static void create(Start start, Connection sqlCon, TenantConfig tenantCon pst.setBoolean(5, tenantConfig.emailPasswordConfig.enabled); pst.setBoolean(6, tenantConfig.passwordlessConfig.enabled); pst.setBoolean(7, tenantConfig.thirdPartyConfig.enabled); + pst.setBoolean(8, tenantConfig.firstFactors == null); + pst.setBoolean(9, tenantConfig.thirdPartyConfig.providers == null); }); } diff --git a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java index 591a4ac0..8aeddc0e 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java @@ -44,7 +44,6 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Iterator; -import java.util.List; import java.util.Scanner; import static org.junit.Assert.*; diff --git a/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java b/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java new file mode 100644 index 00000000..21064f15 --- /dev/null +++ b/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java @@ -0,0 +1,164 @@ +/* + * 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.test; + +import io.supertokens.ProcessState; +import io.supertokens.storage.postgresql.annotations.DashboardInfo; +import io.supertokens.storage.postgresql.config.PostgreSQLConfig; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import static org.junit.Assert.*; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class PostgresSQLConfigTest { + + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void testMatchConfigPropertiesDescription() throws Exception { + String[] args = { "../" }; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + // Skipping postgresql_config_version because it doesn't + // have a description in the config.yaml file + String[] ignoredProperties = { "postgresql_config_version" }; + + // Match the descriptions in the config.yaml file with the descriptions in the + // CoreConfig class + matchYamlAndConfigDescriptions("./config.yaml", ignoredProperties); + + // Match the descriptions in the devConfig.yaml file with the descriptions in + // the CoreConfig class + String[] devConfigIgnoredProperties = Arrays.copyOf(ignoredProperties, ignoredProperties.length + 2); + // We ignore these properties in devConfig.yaml because it has a different + // description + // in devConfig.yaml and has a default value + devConfigIgnoredProperties[ignoredProperties.length] = "postgresql_user"; + devConfigIgnoredProperties[ignoredProperties.length + 1] = "postgresql_password"; + matchYamlAndConfigDescriptions("./devConfig.yaml", devConfigIgnoredProperties); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + + private void matchYamlAndConfigDescriptions(String path, String[] ignoreProperties) throws Exception { + try (BufferedReader reader = new BufferedReader(new FileReader(path))) { + // Get the content of the file as string + String content = reader.lines().collect(Collectors.joining(System.lineSeparator())); + // Find the line that contains 'postgresql_config_version', and then split + // the file after that line + String allProperties = content.split("postgresql_config_version:\\s*\\d+\n")[1]; + + // Split by all the other allProperties string by new line + String[] properties = allProperties.split("\n\n"); + // This will contain the description of each property from the yaml file + Map propertyDescriptions = new HashMap(); + + System.out.println("Last property: " + properties[properties.length - 1] + "\n\n"); + + for (int i = 0; i < properties.length; i++) { + String possibleProperty = properties[i].trim(); + String[] lines = possibleProperty.split("\n"); + // This ensures that it is a property with a description as a comment + // at the top + if (lines[lines.length - 1].endsWith(":")) { + String propertyKeyString = lines[lines.length - 1]; + // Remove the comment "# " from the start + String propertyKey = propertyKeyString.substring(2, propertyKeyString.length() - 1); + String propertyDescription = ""; + // Remove the comment "# " from the start and merge all the lines to form the + // description + for (int j = 0; j < lines.length - 1; j++) { + propertyDescription = propertyDescription + " " + lines[j].substring(2); + } + propertyDescription = propertyDescription.trim(); + + propertyDescriptions.put(propertyKey, propertyDescription); + } + } + + for (String fieldId : PostgreSQLConfig.getValidFields()) { + if (Arrays.asList(ignoreProperties).contains(fieldId)) { + continue; + } + + Field field = PostgreSQLConfig.class.getDeclaredField(fieldId); + + // Skip fields that are not annotated with JsonProperty + if (!field.isAnnotationPresent(JsonProperty.class)) { + continue; + } + + + String valueInfo = ""; + if (field.getType() == String.class) { + valueInfo = "string value."; + } else if (field.getType() == int.class || field.getType() == Integer.class) { + valueInfo = "integer value."; + } else if (field.getType() == long.class) { + valueInfo = "long value."; + } else if (field.getType() == boolean.class || field.getType() == Boolean.class) { + valueInfo = "boolean value."; + } + + String descriptionInConfig = field.getAnnotation(DashboardInfo.class).description(); + descriptionInConfig = "(DIFFERENT_ACROSS_TENANTS" + (field.getAnnotation(DashboardInfo.class).isOptional() ? " | OPTIONAL" : " | COMPULSORY") + + (field.getAnnotation(DashboardInfo.class).isOptional() ? " | Default: " + field.getAnnotation(DashboardInfo.class).defaultValue() : "") + + ") " + + valueInfo + " " + + descriptionInConfig; + String descriptionInYaml = propertyDescriptions.get(fieldId); + + if (descriptionInYaml == null) { + fail("Unable to find description or property for " + fieldId + " in " + path + " file"); + } + + // Assert that description in yaml contains the description in config + assertEquals("For " + fieldId, descriptionInYaml.trim(), descriptionInConfig.trim()); + } + } + } + +} diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java index 97a3fb6b..77204865 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java @@ -49,7 +49,6 @@ import java.io.IOException; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; From bac79ce1fecbc89642befb791bf5b8ca6b809b7c Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 24 May 2024 16:10:53 +0530 Subject: [PATCH 02/45] adding dev-v7.1.0 tag to this commit to ensure building --- ...-7.0.1.jar => postgresql-plugin-7.1.0.jar} | Bin 220474 -> 223814 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename jar/{postgresql-plugin-7.0.1.jar => postgresql-plugin-7.1.0.jar} (73%) diff --git a/jar/postgresql-plugin-7.0.1.jar b/jar/postgresql-plugin-7.1.0.jar similarity index 73% rename from jar/postgresql-plugin-7.0.1.jar rename to jar/postgresql-plugin-7.1.0.jar index 7437a72213c54cd456c49a709b8bd5f1f27762d8..a6fb21b003af1614614828daf54192a9f493cc3f 100644 GIT binary patch delta 53193 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

(Mc9y-W=!01q>f1= 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 From 1c6b8b724ade7243c167e6106486bb678ae6c285 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 8 Jul 2024 21:22:05 +0530 Subject: [PATCH 03/45] reformats code --- .github/PULL_REQUEST_TEMPLATE.md | 23 +- .github/helpers/package.json | 24 +- .../workflows/github-actions-changelog.yml | 12 +- .github/workflows/lint-pr-title.yml | 30 +- .github/workflows/tests-pass-check-pr.yml | 38 +- .github/workflows/tests.yml | 8 +- CHANGELOG.md | 60 +-- CONTRIBUTING.md | 57 ++- LICENSE.md | 371 +++++++++--------- README.md | 14 +- .../storage/postgresql/ConnectionPool.java | 3 +- .../postgresql/QueryExecutorTemplate.java | 4 +- .../supertokens/storage/postgresql/Start.java | 56 ++- .../storage/postgresql/config/Config.java | 3 +- .../postgresql/config/PostgreSQLConfig.java | 20 +- .../queries/ActiveUsersQueries.java | 47 +-- .../postgresql/queries/DashboardQueries.java | 19 +- .../queries/EmailPasswordQueries.java | 33 +- .../queries/EmailVerificationQueries.java | 20 +- .../postgresql/queries/GeneralQueries.java | 140 ++++--- .../postgresql/queries/JWTSigningQueries.java | 8 +- .../queries/MultitenancyQueries.java | 64 +-- .../queries/PasswordlessQueries.java | 42 +- .../postgresql/queries/TOTPQueries.java | 33 +- .../postgresql/queries/ThirdPartyQueries.java | 26 +- .../queries/UserIdMappingQueries.java | 45 ++- .../postgresql/queries/UserRolesQueries.java | 2 +- .../queries/multitenancy/MfaSqlHelper.java | 23 +- .../multitenancy/TenantConfigSQLHelper.java | 31 +- .../ThirdPartyProviderClientSQLHelper.java | 34 +- .../ThirdPartyProviderSQLHelper.java | 47 ++- .../storage/postgresql/test/ConfigTest.java | 7 +- .../postgresql/test/DbConnectionPoolTest.java | 7 +- .../storage/postgresql/test/DeadlockTest.java | 21 +- .../storage/postgresql/test/LogLevelTest.java | 10 +- .../storage/postgresql/test/LoggingTest.java | 30 +- .../postgresql/test/OneMillionUsersTest.java | 26 +- .../postgresql/test/StorageLayerTest.java | 20 +- .../test/SuperTokensSaaSSecretTest.java | 30 +- .../postgresql/test/TableCreationTest.java | 2 +- .../postgresql/test/TestMainThread.java | 5 +- .../httpRequest/HttpRequestForTesting.java | 141 +++---- .../test/multitenancy/StorageLayerTest.java | 6 +- .../TestForNoCrashDuringStartup.java | 73 ++-- 44 files changed, 1011 insertions(+), 704 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4de1c9d4..2b22ae85 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,27 +1,38 @@ ## Summary of change + (A few sentences about this PR) ## Related issues + - Link to issue1 here - Link to issue1 here ## Test Plan -(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!) + +(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your +changes work. Bonus points for screenshots and videos!) ## Documentation changes -(If relevant, please create a PR in our [docs repo](https://github.com/supertokens/docs), or create a checklist here highlighting the necessary changes) + +(If relevant, please create a PR in our [docs repo](https://github.com/supertokens/docs), or create a checklist here +highlighting the necessary changes) ## Checklist for important updates + - [ ] Changelog has been updated - [ ] `pluginInterfaceSupported.json` file has been updated (if needed) - [ ] Changes to the version if needed - - In `build.gradle` + - In `build.gradle` - [ ] Had installed and ran the pre-commit hook -- [ ] If there are new dependencies that have been added in `build.gradle`, please make sure to add them in `implementationDependencies.json`. +- [ ] If there are new dependencies that have been added in `build.gradle`, please make sure to add them + in `implementationDependencies.json`. - [ ] Issue this PR against the latest non released version branch. - - To know which one it is, run find the latest released tag (`git tag`) in the format `vX.Y.Z`, and then find the latest branch (`git branch --all`) whose `X.Y` is greater than the latest released tag. - - If no such branch exists, then create one from the latest released branch. + - To know which one it is, run find the latest released tag (`git tag`) in the format `vX.Y.Z`, and then find the + latest branch (`git branch --all`) whose `X.Y` is greater than the latest released tag. + - If no such branch exists, then create one from the latest released branch. - [ ] When adding new recipes, ensure that its performance is being measured in the `OneMillionUsersTest` + ## Remaining TODOs for this PR + - [ ] Item1 - [ ] Item2 \ No newline at end of file diff --git a/.github/helpers/package.json b/.github/helpers/package.json index 80ec546b..28051cdb 100644 --- a/.github/helpers/package.json +++ b/.github/helpers/package.json @@ -1,14 +1,14 @@ { - "name": "helpers", - "version": "1.0.0", - "description": "", - "main": "test-pass-check-pr.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "dependencies": { - "github-workflow-helpers": "github:supertokens/github-workflow-helpers" - } + "name": "helpers", + "version": "1.0.0", + "description": "", + "main": "test-pass-check-pr.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "github-workflow-helpers": "github:supertokens/github-workflow-helpers" + } } \ No newline at end of file diff --git a/.github/workflows/github-actions-changelog.yml b/.github/workflows/github-actions-changelog.yml index 0007ca9a..47aaf4a2 100644 --- a/.github/workflows/github-actions-changelog.yml +++ b/.github/workflows/github-actions-changelog.yml @@ -1,15 +1,15 @@ name: "Enforcing changelog in PRs Workflow" on: pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] + types: [ opened, synchronize, reopened, ready_for_review, labeled, unlabeled ] jobs: # Enforces the update of a changelog file on every pull request changelog: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: dangoslen/changelog-enforcer@v2 - with: - changeLogPath: 'CHANGELOG.md' - skipLabels: 'Skip-Changelog' \ No newline at end of file + - uses: actions/checkout@v2 + - uses: dangoslen/changelog-enforcer@v2 + with: + changeLogPath: 'CHANGELOG.md' + skipLabels: 'Skip-Changelog' \ No newline at end of file diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index 904efde8..96281a74 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -1,20 +1,20 @@ name: "Lint PR Title" on: - pull_request: - types: - - opened - - reopened - - edited - - synchronize + pull_request: + types: + - opened + - reopened + - edited + - synchronize jobs: - pr-title: - name: Lint PR title - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - validateSingleCommit: true \ No newline at end of file + pr-title: + name: Lint PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + validateSingleCommit: true \ No newline at end of file diff --git a/.github/workflows/tests-pass-check-pr.yml b/.github/workflows/tests-pass-check-pr.yml index 4f03705b..07467d9d 100644 --- a/.github/workflows/tests-pass-check-pr.yml +++ b/.github/workflows/tests-pass-check-pr.yml @@ -1,24 +1,24 @@ name: "Check if \"Run tests\" action succeeded" on: - pull_request: - types: - - opened - - reopened - - edited - - synchronize + pull_request: + types: + - opened + - reopened + - edited + - synchronize jobs: - pr-run-test-action: - name: Check if "Run tests" action succeeded - timeout-minutes: 60 - concurrency: - group: ${{ github.head_ref }} - cancel-in-progress: true - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: node install - run: cd ./.github/helpers && npm i - - name: Calling github API - run: cd ./.github/helpers && GITHUB_TOKEN=${{ github.token }} REPO=${{ github.repository }} RUN_ID=${{ github.run_id }} BRANCH=${{ github.head_ref }} JOB_ID=${{ github.job }} SOURCE_OWNER=${{ github.event.pull_request.head.repo.owner.login }} CURRENT_SHA=${{ github.event.pull_request.head.sha }} node node_modules/github-workflow-helpers/test-pass-check-pr.js \ No newline at end of file + pr-run-test-action: + name: Check if "Run tests" action succeeded + timeout-minutes: 60 + concurrency: + group: ${{ github.head_ref }} + cancel-in-progress: true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: node install + run: cd ./.github/helpers && npm i + - name: Calling github API + run: cd ./.github/helpers && GITHUB_TOKEN=${{ github.token }} REPO=${{ github.repository }} RUN_ID=${{ github.run_id }} BRANCH=${{ github.head_ref }} JOB_ID=${{ github.job }} SOURCE_OWNER=${{ github.event.pull_request.head.repo.owner.login }} CURRENT_SHA=${{ github.event.pull_request.head.sha }} node node_modules/github-workflow-helpers/test-pass-check-pr.js \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8bb11ba3..dc2133e9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,19 +4,19 @@ on: inputs: coreRepoOwnerName: description: 'supertokens-core repo owner name' - default: supertokens + default: supertokens required: true coreRepoBranch: description: 'supertokens-core repos branch name' - default: master + default: master required: true pluginRepoOwnerName: description: 'supertokens-plugin-interface repo owner name' - default: supertokens + default: supertokens required: true pluginInterfaceBranch: description: 'supertokens-plugin-interface repos branch name' - default: master + default: master required: true jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index a99afbf0..84619c65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,8 +17,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Support for MFA recipe - Adds `firstFactors` and `requiredSecondaryFactors` for tenant config. - Adds a new `useStaticKey` param to `updateSessionInfo_Transaction` - - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to - change the signing key type of a session + - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to + change the signing key type of a session ### Migration @@ -49,7 +49,8 @@ ALTER TABLE user_roles DROP CONSTRAINT IF EXISTS user_roles_role_fkey; - Fixes the issue where passwords were inadvertently logged in the logs. - Adds tests to check connection pool behaviour. -- Adds `postgresql_idle_connection_timeout` and `postgresql_minimum_idle_connections` configs to control active connections to the database. +- Adds `postgresql_idle_connection_timeout` and `postgresql_minimum_idle_connections` configs to control active + connections to the database. ## [5.0.6] - 2023-12-05 @@ -89,15 +90,16 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ ### Changes - Support for Account Linking - - Adds columns `primary_or_recipe_user_id`, `is_linked_or_is_a_primary_user` and `primary_or_recipe_user_time_joined` to `all_auth_recipe_users` table - - Adds columns `primary_or_recipe_user_id` and `is_linked_or_is_a_primary_user` to `app_id_to_user_id` table - - Removes index `all_auth_recipe_users_pagination_index` and addes `all_auth_recipe_users_pagination_index1`, - `all_auth_recipe_users_pagination_index2`, `all_auth_recipe_users_pagination_index3` and - `all_auth_recipe_users_pagination_index4` indexes instead on `all_auth_recipe_users` table - - Adds `all_auth_recipe_users_recipe_id_index` on `all_auth_recipe_users` table - - Adds `all_auth_recipe_users_primary_user_id_index` on `all_auth_recipe_users` table - - Adds `email` column to `emailpassword_pswd_reset_tokens` table - - Changes `user_id` foreign key constraint on `emailpassword_pswd_reset_tokens` to `app_id_to_user_id` table + - Adds columns `primary_or_recipe_user_id`, `is_linked_or_is_a_primary_user` + and `primary_or_recipe_user_time_joined` to `all_auth_recipe_users` table + - Adds columns `primary_or_recipe_user_id` and `is_linked_or_is_a_primary_user` to `app_id_to_user_id` table + - Removes index `all_auth_recipe_users_pagination_index` and addes `all_auth_recipe_users_pagination_index1`, + `all_auth_recipe_users_pagination_index2`, `all_auth_recipe_users_pagination_index3` and + `all_auth_recipe_users_pagination_index4` indexes instead on `all_auth_recipe_users` table + - Adds `all_auth_recipe_users_recipe_id_index` on `all_auth_recipe_users` table + - Adds `all_auth_recipe_users_primary_user_id_index` on `all_auth_recipe_users` table + - Adds `email` column to `emailpassword_pswd_reset_tokens` table + - Changes `user_id` foreign key constraint on `emailpassword_pswd_reset_tokens` to `app_id_to_user_id` table ### Migration @@ -178,21 +180,20 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ - Fixes null pointer issue when user belongs to no tenant. - ## [4.0.1] - 2023-07-11 - Fixes duplicate users in users search queries when user is associated to multiple tenants - ## [4.0.0] - 2023-06-02 ### Changes - Support for multitenancy - - New tables `apps` and `tenants` have been added. - - Schema of tables have been changed, adding `app_id` and `tenant_id` columns in tables and constraints & indexes have been modified to include this columns. - - New user tables have been added to map users to apps and tenants. - - New tables for multitenancy have been added. + - New tables `apps` and `tenants` have been added. + - Schema of tables have been changed, adding `app_id` and `tenant_id` columns in tables and constraints & indexes + have been modified to include this columns. + - New user tables have been added to map users to apps and tenants. + - New tables for multitenancy have been added. - Increased transaction retry count to 50 from 20. ### Migration @@ -1070,19 +1071,19 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ ### Migration - If using `access_token_signing_key_dynamic` false in the core: - - ```sql - ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(true); - ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; + - ```sql + ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(true); + ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; ``` - - ```sql + - ```sql INSERT INTO jwt_signing_keys(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 session_access_token_signing_keys; ``` - If using `access_token_signing_key_dynamic` true (or not set) in the core: - - ```sql - ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(false); - ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; + - ```sql + ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(false); + ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; ``` ## [2.4.0] - 2023-03-30 @@ -1090,14 +1091,17 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ - Support for Dashboard Search ## [2.3.0] - 2023-03-27 + - Support for TOTP recipe - Support for active users ### Database changes + - Add new tables for TOTP recipe: - - `totp_users` that stores the users that have enabled TOTP - - `totp_user_devices` that stores devices (each device has its own secret) for each user - - `totp_used_codes` that stores used codes for each user. This is to implement rate limiting and prevent replay attacks. + - `totp_users` that stores the users that have enabled TOTP + - `totp_user_devices` that stores devices (each device has its own secret) for each user + - `totp_used_codes` that stores used codes for each user. This is to implement rate limiting and prevent replay + attacks. - Add `user_last_active` table to store the last active time of a user. ## [2.2.0] - 2023-02-21 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53732985..a222aea4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,51 +1,70 @@ - # Contributing -We're so excited you're interested in helping with SuperTokens! We are happy to help you get started, even if you don't have any previous open-source experience :blush: +We're so excited you're interested in helping with SuperTokens! We are happy to help you get started, even if you don't +have any previous open-source experience :blush: ## New to Open Source? -1. Take a look at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) -2. Go thorugh the [SuperTokens Code of Conduct](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CODE_OF_CONDUCT.md) + +1. Take a look + at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) +2. Go thorugh + the [SuperTokens Code of Conduct](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CODE_OF_CONDUCT.md) ## Where to ask Questions? -1. Check our [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) to see if someone has already answered your question. -2. Join our community on [Discord](https://supertokens.io/discord) and feel free to ask us your questions +1. Check our [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) to see if someone has + already answered your question. +2. Join our community on [Discord](https://supertokens.io/discord) and feel free to ask us your questions ## Development Setup + ### Prerequisites + - OS: Linux or macOS - IDE: Intellij (recommended) or equivalent IDE - PostgreSQL ### Project Setup -1. Setup the `supertokens-core` by following [this guide](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#development-setup). If you are not modifying the `supertokens-core` repo, then you do not need to fork that. + +1. Setup the `supertokens-core` by + following [this guide](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#development-setup). + If you are not modifying the `supertokens-core` repo, then you do not need to fork that. 2. Start PostgreSQL on port `5432`, listening to `locahost` or `0.0.0.0`. 3. Create a PostgreSQL user (if not already exists) with username `root` and password `root` 4. Create a database called `supertokens`. 5. Fork the `supertokens-pstgresql-plugin` repository -6. Open `modules.txt` in the `supertokens-root` directory and change it so that it looks like (the last line has changed): +6. Open `modules.txt` in the `supertokens-root` directory and change it so that it looks like (the last line has + changed): ``` // put module name like module name,branch name,github username(if contributing with a forked repository) and then call ./loadModules script core,master plugin-interface,master postgresql-plugin,master, ``` -7. Run `./loadModules` in the `supertokens-root` directory. This will clone your forked `supertokens-postgresql-plugin` repo. -8. Follow the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#modifying-code) guide from `supertokens-core` repo for modifying and testing. +7. Run `./loadModules` in the `supertokens-root` directory. This will clone your forked `supertokens-postgresql-plugin` + repo. +8. Follow + the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#modifying-code) + guide from `supertokens-core` repo for modifying and testing. ## Pull Request + 1. Before submitting a pull request make sure all tests have passed -2. Reference the relevant issue or pull request and give a clear description of changes/features added when submitting a pull request +2. Reference the relevant issue or pull request and give a clear description of changes/features added when submitting a + pull request 3. Make sure the PR title follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) specification ## SuperTokens Community -SuperTokens is made possible by a passionate team and a strong community of developers. If you have any questions or would like to get more involved in the SuperTokens community you can check out: - - [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) - - [Discord](https://supertokens.io/discord) - - [Twitter](https://twitter.com/supertokensio) - - or [email us](mailto:team@supertokens.io) - + +SuperTokens is made possible by a passionate team and a strong community of developers. If you have any questions or +would like to get more involved in the SuperTokens community you can check out: + +- [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) +- [Discord](https://supertokens.io/discord) +- [Twitter](https://twitter.com/supertokensio) +- or [email us](mailto:team@supertokens.io) + Additional resources you might find useful: - - [SuperTokens Docs](https://supertokens.io/docs/community/getting-started/installation) - - [Blog Posts](https://supertokens.io/blog/) + +- [SuperTokens Docs](https://supertokens.io/docs/community/getting-started/installation) +- [Blog Posts](https://supertokens.io/blog/) diff --git a/LICENSE.md b/LICENSE.md index e105852e..7a85c9c7 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,191 +1,192 @@ - Copyright (c) 2020, 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 software except in compliance with the License. A copy - of the License is available below the line. - - 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. +Copyright (c) 2020, 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 software except in compliance with the License. A copy +of the License is available below the line. + +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. ------------------------------------------------------------------------------- + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md index cc88cb0b..d44d464f 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,32 @@ - ![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png) # PostgreSQL plugin for SuperTokens Community + chat on Discord ## About + This plugin is responsible for interfacing between SuperTokens Community version and an instance of PostgreSQL. Learn more at https://supertokens.io ## Documentation + To see documentation, please click [here](https://supertokens.io/docs/community/introduction). ## Contributing -Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CONTRIBUTING.md) file in this repo. + +Please refer to +the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CONTRIBUTING.md) file in +this repo. ## Contact us -For any queries, or support requests, please email us at team@supertokens.io, or join our [Discord](supertokens.io/discord) server. + +For any queries, or support requests, please email us at team@supertokens.io, or join +our [Discord](supertokens.io/discord) server. # Authors + Created with :heart: by the folks at SuperTokens.io. diff --git a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java index 534e706a..acab61f5 100644 --- a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java +++ b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java @@ -149,7 +149,8 @@ static boolean isAlreadyInitialised(Start start) { return getInstance(start) != null && getInstance(start).hikariDataSource != null; } - static void initPool(Start start, boolean shouldWait, PostConnectCallback postConnectCallback) throws DbInitException { + static void initPool(Start start, boolean shouldWait, PostConnectCallback postConnectCallback) + throws DbInitException { if (isAlreadyInitialised(start)) { return; } diff --git a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java index db0c9785..179a871b 100644 --- a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java +++ b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java @@ -26,14 +26,14 @@ public interface QueryExecutorTemplate { static T execute(Start start, String QUERY, PreparedStatementValueSetter setter, - ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { + ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { try (Connection con = ConnectionPool.getConnection(start)) { return execute(con, QUERY, setter, mapper); } } static T execute(Connection con, String QUERY, PreparedStatementValueSetter setter, - ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { + ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { if (setter == null) setter = PreparedStatementValueSetter.NO_OP_SETTER; try (PreparedStatement pst = con.prepareStatement(QUERY)) { diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 82832bb5..103bde0d 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -841,7 +841,8 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi } } else if (className.equals(TOTPStorage.class.getName())) { try { - TOTPDevice device = new TOTPDevice(userId, "testDevice", "secret", 0, 30, false, System.currentTimeMillis()); + TOTPDevice device = new TOTPDevice(userId, "testDevice", "secret", 0, 30, false, + System.currentTimeMillis()); this.startTransaction(con -> { try { long now = System.currentTimeMillis(); @@ -884,7 +885,7 @@ public String[] getProtectedConfigsFromSuperTokensSaaSUsers() { @Override public AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, String email, String passwordHash, - long timeJoined) + long timeJoined) throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, TenantOrAppNotFoundException { try { @@ -1194,8 +1195,10 @@ public boolean isEmailVerified(AppIdentifier appIdentifier, String userId, Strin } @Override - public void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, String supertokensUserId, String externalUserId) throws StorageQueryException { - EmailVerificationQueries.updateIsEmailVerifiedToExternalUserId(this, appIdentifier, supertokensUserId, externalUserId); + public void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, String supertokensUserId, + String externalUserId) throws StorageQueryException { + EmailVerificationQueries.updateIsEmailVerifiedToExternalUserId(this, appIdentifier, supertokensUserId, + externalUserId); } @Override @@ -1716,10 +1719,10 @@ public void createCode(TenantIdentifier tenantIdentifier, PasswordlessCode code) @Override public AuthRecipeUserInfo createUser(TenantIdentifier tenantIdentifier, - String id, - @javax.annotation.Nullable String email, - @javax.annotation.Nullable - String phoneNumber, long timeJoined) + String id, + @javax.annotation.Nullable String email, + @javax.annotation.Nullable + String phoneNumber, long timeJoined) throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, DuplicateUserIdException, TenantOrAppNotFoundException { @@ -2240,7 +2243,8 @@ public boolean updateOrDeleteExternalUserIdInfo(AppIdentifier appIdentifier, Str } @Override - public HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier, ArrayList userIds) + public HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier, + ArrayList userIds) throws StorageQueryException { try { return UserIdMappingQueries.getUserIdMappingWithUserIds(this, appIdentifier, userIds); @@ -2359,7 +2363,8 @@ public TenantConfig[] getAllTenants() throws StorageQueryException { } @Override - public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId) + public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String userId) throws TenantOrAppNotFoundException, UnknownUserIdException, StorageQueryException, DuplicateEmailException, DuplicateThirdPartyUserException, DuplicatePhoneNumberException { Connection sqlCon = (Connection) con.getConnection(); @@ -2658,7 +2663,8 @@ public void createDevice(AppIdentifier appIdentifier, TOTPDevice device) } @Override - public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, TOTPDevice device) + public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + TOTPDevice device) throws StorageQueryException, DeviceAlreadyExistsException, TenantOrAppNotFoundException { Connection sqlCon = (Connection) con.getConnection(); try { @@ -2671,9 +2677,9 @@ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentif ServerErrorMessage errMsg = ((PSQLException) actualException).getServerErrorMessage(); if (isPrimaryKeyError(errMsg, Config.getConfig(this).getTotpUserDevicesTable())) { - throw new DeviceAlreadyExistsException(); + throw new DeviceAlreadyExistsException(); } else if (isForeignKeyConstraintError(errMsg, Config.getConfig(this).getTotpUsersTable(), "app_id")) { - throw new TenantOrAppNotFoundException(appIdentifier); + throw new TenantOrAppNotFoundException(appIdentifier); } } throw new StorageQueryException(e); @@ -2681,7 +2687,8 @@ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentif } @Override - public TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, String deviceName) throws StorageQueryException { + public TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, + String deviceName) throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); try { return TOTPQueries.getDeviceByName_Transaction(this, sqlCon, appIdentifier, userId, deviceName); @@ -2751,7 +2758,7 @@ public void updateDeviceName(AppIdentifier appIdentifier, String userId, String if (e instanceof PSQLException) { ServerErrorMessage errMsg = ((PSQLException) e).getServerErrorMessage(); if (isPrimaryKeyError(errMsg, Config.getConfig(this).getTotpUserDevicesTable())) { - throw new DeviceAlreadyExistsException(); + throw new DeviceAlreadyExistsException(); } } throw new StorageQueryException(e); @@ -2954,7 +2961,8 @@ public void linkAccounts_Transaction(AppIdentifier appIdentifier, TransactionCon } @Override - public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, String recipeUserId) + public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, + String recipeUserId) throws StorageQueryException { try { Connection sqlCon = (Connection) con.getConnection(); @@ -2987,7 +2995,8 @@ public boolean checkIfUsesAccountLinking(AppIdentifier appIdentifier) throws Sto } @Override - public int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException { + public int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) + throws StorageQueryException { try { return ActiveUsersQueries.countUsersActiveSinceAndHasMoreThanOneLoginMethod(this, appIdentifier, sinceTime); } catch (SQLException e) { @@ -3016,11 +3025,13 @@ public UserIdMapping getUserIdMapping_Transaction(TransactionConnection con, App try { Connection sqlCon = (Connection) con.getConnection(); if (isSuperTokensUserId) { - return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId_Transaction(this, sqlCon, appIdentifier, + return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId_Transaction(this, sqlCon, + appIdentifier, userId); } - return UserIdMappingQueries.getUserIdMappingWithExternalUserId_Transaction(this, sqlCon, appIdentifier, userId); + return UserIdMappingQueries.getUserIdMappingWithExternalUserId_Transaction(this, sqlCon, appIdentifier, + userId); } catch (SQLException e) { throw new StorageQueryException(e); } @@ -3041,7 +3052,8 @@ public UserIdMapping[] getUserIdMapping_Transaction(TransactionConnection con, A } @Override - public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier) throws StorageQueryException { + public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier) + throws StorageQueryException { try { return GeneralQueries.getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(this, appIdentifier); } catch (SQLException e) { @@ -3050,7 +3062,9 @@ public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier ap } @Override - public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException { + public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(AppIdentifier appIdentifier, + long sinceTime) + throws StorageQueryException { try { return ActiveUsersQueries.countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(this, appIdentifier, sinceTime); diff --git a/src/main/java/io/supertokens/storage/postgresql/config/Config.java b/src/main/java/io/supertokens/storage/postgresql/config/Config.java index 41088d73..1ce21e06 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/Config.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/Config.java @@ -53,7 +53,8 @@ private static Config getInstance(Start start) { return (Config) start.getResourceDistributor().getResource(RESOURCE_KEY); } - public static void loadConfig(Start start, JsonObject configJson, Set logLevels, TenantIdentifier tenantIdentifier) + public static void loadConfig(Start start, JsonObject configJson, Set logLevels, + TenantIdentifier tenantIdentifier) throws InvalidConfigException { if (getInstance(start) != null) { return; 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 e0a0c682..7bb71715 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -522,21 +522,26 @@ public void validateAndNormalise() throws InvalidConfigException { } if (postgresql_emailpassword_pswd_reset_tokens_table_name != null) { - postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaToTableName(postgresql_emailpassword_pswd_reset_tokens_table_name); + postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaToTableName( + postgresql_emailpassword_pswd_reset_tokens_table_name); } else { - postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaAndPrefixToTableName("emailpassword_pswd_reset_tokens"); + postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaAndPrefixToTableName( + "emailpassword_pswd_reset_tokens"); } if (postgresql_emailverification_tokens_table_name != null) { - postgresql_emailverification_tokens_table_name = addSchemaToTableName(postgresql_emailverification_tokens_table_name); + postgresql_emailverification_tokens_table_name = addSchemaToTableName( + postgresql_emailverification_tokens_table_name); } else { postgresql_emailverification_tokens_table_name = addSchemaAndPrefixToTableName("emailverification_tokens"); } if (postgresql_emailverification_verified_emails_table_name != null) { - postgresql_emailverification_verified_emails_table_name = addSchemaToTableName(postgresql_emailverification_verified_emails_table_name); + postgresql_emailverification_verified_emails_table_name = addSchemaToTableName( + postgresql_emailverification_verified_emails_table_name); } else { - postgresql_emailverification_verified_emails_table_name = addSchemaAndPrefixToTableName("emailverification_verified_emails"); + postgresql_emailverification_verified_emails_table_name = addSchemaAndPrefixToTableName( + "emailverification_verified_emails"); } if (postgresql_thirdparty_users_table_name != null) { @@ -588,10 +593,11 @@ public String getConnectionPoolId() { try { String fieldName = field.getName(); String fieldValue = field.get(this) != null ? field.get(this).toString() : null; - if(fieldValue == null) { + if (fieldValue == null) { continue; } - // To ensure a unique connectionPoolId we include the database password and use the "|db_pass|" identifier. + // To ensure a unique connectionPoolId we include the database password and use the "|db_pass|" + // identifier. // This facilitates easy removal of the password from logs when necessary. if (fieldName.equals("postgresql_password")) { connectionPoolId.append("|db_pass|" + fieldValue + "|db_pass"); 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 3a39c384..327ed6ce 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java @@ -50,7 +50,8 @@ public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier }); } - public static int countUsersActiveSinceAndHasMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier, long sinceTime) + public static int countUsersActiveSinceAndHasMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier, + long sinceTime) throws SQLException, StorageQueryException { // TODO: Active users are present only on public tenant and MFA users may be present on different storages String QUERY = "SELECT count(1) as c FROM (" @@ -121,30 +122,32 @@ public static void deleteUserActive_Transaction(Connection con, Start start, App }); } - public static int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) + public static int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(Start start, + AppIdentifier appIdentifier, + long sinceTime) throws SQLException, StorageQueryException { // TODO: Active users are present only on public tenant and MFA users may be present on different storages String QUERY = - "SELECT COUNT (DISTINCT user_id) as c FROM (" - + " (" // users with more than one login method - + " SELECT primary_or_recipe_user_id AS user_id FROM (" - + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" - + " FROM " + getConfig(start).getAppIdToUserIdTable() - + " WHERE app_id = ? AND primary_or_recipe_user_id IN (" - + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() - + " WHERE app_id = ? AND last_active_time >= ?" - + " )" - + " GROUP BY (app_id, primary_or_recipe_user_id)" - + " ) AS nloginmethods" - + " WHERE num_login_methods > 1" - + " ) UNION (" // TOTP users - + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() - + " WHERE app_id = ? AND user_id IN (" - + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() - + " WHERE app_id = ? AND last_active_time >= ?" - + " )" - + " )" - + ") AS all_users"; + "SELECT COUNT (DISTINCT user_id) as c FROM (" + + " (" // users with more than one login method + + " SELECT primary_or_recipe_user_id AS user_id FROM (" + + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" + + " FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? AND primary_or_recipe_user_id IN (" + + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() + + " WHERE app_id = ? AND last_active_time >= ?" + + " )" + + " GROUP BY (app_id, primary_or_recipe_user_id)" + + " ) AS nloginmethods" + + " WHERE num_login_methods > 1" + + " ) UNION (" // TOTP users + + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() + + " WHERE app_id = ? AND user_id IN (" + + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() + + " WHERE app_id = ? AND last_active_time >= ?" + + " )" + + " )" + + ") AS all_users"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); 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..89e85607 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java @@ -51,7 +51,7 @@ public static String getQueryToCreateDashboardUsersTable(Start start) { + " 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" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -119,7 +119,8 @@ public static boolean deleteDashboardUserWithUserId(Start start, AppIdentifier a } - public static DashboardUser[] getAllDashBoardUsers(Start start, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { + public static DashboardUser[] getAllDashBoardUsers(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getDashboardUsersTable() + " WHERE app_id = ? ORDER BY time_joined ASC"; return QueryExecutorTemplate.execute(start, QUERY, @@ -172,8 +173,9 @@ public static boolean updateDashboardUsersPasswordWithUserId_Transaction(Start s return rowsUpdated > 0; } - public static void createDashboardSession(Start start, AppIdentifier appIdentifier, String userId, String sessionId, long timeCreated, - long expiry) throws SQLException, StorageQueryException { + public static void createDashboardSession(Start start, AppIdentifier appIdentifier, String userId, String sessionId, + long timeCreated, + long expiry) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + Config.getConfig(start).getDashboardSessionsTable() + "(app_id, user_id, session_id, time_created, expiry)" + " VALUES(?, ?, ?, ?, ?)"; QueryExecutorTemplate.update(start, QUERY, pst -> { @@ -185,7 +187,8 @@ public static void createDashboardSession(Start start, AppIdentifier appIdentifi }); } - public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppIdentifier appIdentifier, String sessionId) + public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppIdentifier appIdentifier, + String sessionId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND session_id = ?"; @@ -200,7 +203,8 @@ public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppI }); } - public static DashboardSessionInfo[] getAllSessionsForUserId(Start start, AppIdentifier appIdentifier, String userId) + public static DashboardSessionInfo[] getAllSessionsForUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND user_id = ?"; @@ -234,7 +238,8 @@ public static DashboardUser getDashboardUserByEmail(Start start, AppIdentifier a }); } - public static boolean deleteDashboardUserSessionWithSessionId(Start start, AppIdentifier appIdentifier, String sessionId) + public static boolean deleteDashboardUserSessionWithSessionId(Start start, AppIdentifier appIdentifier, + String sessionId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND session_id = ?"; 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..efed6c7f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java @@ -285,7 +285,9 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -375,8 +377,10 @@ public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIde } } - private static UserInfoPartial getUserInfoUsingId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, - String id) throws SQLException, StorageQueryException { + private static UserInfoPartial getUserInfoUsingId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String id) + throws SQLException, StorageQueryException { // we don't need a FOR UPDATE here because this is already part of a transaction, and locked on // app_id_to_user_id table String QUERY = "SELECT user_id, email, password_hash, time_joined FROM " @@ -426,7 +430,7 @@ public static List getUsersInfoUsingIdList(Start start, Set } public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, - AppIdentifier appIdentifier) + AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { // No need to filter based on tenantId because the id list is already filtered for a tenant @@ -456,6 +460,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start, } return Collections.emptyList(); } + public static String lockEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String email) @@ -475,7 +480,7 @@ public static String lockEmail_Transaction(Start start, Connection con, } public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, - String email) + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getEmailPasswordUserToTenantTable() + " AS ep" + @@ -495,8 +500,9 @@ public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier te }); } - public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - String email) + public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getEmailPasswordUsersTable() + " AS ep" + @@ -526,11 +532,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC throw new UnknownUserIdException(); } - GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, + sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " + + "recipe_id, time_joined, primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; GeneralQueries.AccountLinkingInfo finalAccountLinkingInfo = accountLinkingInfo; @@ -545,14 +554,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC pst.setLong(8, userInfo.timeJoined); }); - GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), finalAccountLinkingInfo.primaryUserId); + GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), + finalAccountLinkingInfo.primaryUserId); } { // emailpassword_user_to_tenant String QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUserToTenantTable() + "(app_id, tenant_id, user_id, email)" + " VALUES(?, ?, ?, ?) " + " ON CONFLICT ON CONSTRAINT " - + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getEmailPasswordUserToTenantTable(), null, "pkey") + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), + getConfig(start).getEmailPasswordUserToTenantTable(), null, "pkey") + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { 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 6fd00660..9c70cf8f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java @@ -270,8 +270,10 @@ public static List isEmailVerified_transaction(Start start, Connection s // We have external user id stored in the email verification table, so we need to fetch the mapped userids for // calculating the verified emails - HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds_Transaction(start, - sqlCon, appIdentifier, supertokensUserIds); + HashMap supertokensUserIdToExternalUserIdMap = + UserIdMappingQueries.getUserIdMappingWithUserIds_Transaction( + start, + sqlCon, appIdentifier, supertokensUserIds); HashMap externalUserIdToSupertokensUserIdMap = new HashMap<>(); List supertokensOrExternalUserIdsToQuery = new ArrayList<>(); @@ -298,7 +300,8 @@ public static List isEmailVerified_transaction(Start start, Connection s } String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable() - + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + + + " WHERE app_id = ? AND user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + ") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")"; return execute(sqlCon, QUERY, pst -> { @@ -324,7 +327,7 @@ public static List isEmailVerified_transaction(Start start, Connection s } public static List isEmailVerified(Start start, AppIdentifier appIdentifier, - List userIdAndEmail) + List userIdAndEmail) throws SQLException, StorageQueryException { if (userIdAndEmail.isEmpty()) { @@ -339,7 +342,8 @@ public static List isEmailVerified(Start start, AppIdentifier appIdentif } // We have external user id stored in the email verification table, so we need to fetch the mapped userids for // calculating the verified emails - HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds(start, + HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds( + start, appIdentifier, supertokensUserIds); HashMap externalUserIdToSupertokensUserIdMap = new HashMap<>(); List supertokensOrExternalUserIdsToQuery = new ArrayList<>(); @@ -365,7 +369,8 @@ public static List isEmailVerified(Start start, AppIdentifier appIdentif supertokensOrExternalUserIdToEmailMap.put(supertokensOrExternalUserId, ue.email); } String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable() - + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + + + " WHERE app_id = ? AND user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + ") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -476,7 +481,8 @@ public static boolean isUserIdBeingUsedForEmailVerification(Start start, AppIden } } - public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentifier appIdentifier, String supertokensUserId, String externalUserId) + public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentifier appIdentifier, + String supertokensUserId, String externalUserId) throws StorageQueryException { try { start.startTransaction((TransactionConnection con) -> { 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 94b54514..1608d3df 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -56,13 +56,15 @@ public class GeneralQueries { - private static boolean doesTableExists(Start start, Connection connection, String tableName) throws SQLException, StorageQueryException { + private static boolean doesTableExists(Start start, Connection connection, String tableName) + throws SQLException, StorageQueryException { try { String QUERY = "SELECT 1 FROM " + tableName + " LIMIT 1"; execute(connection, QUERY, NO_OP_SETTER, result -> null); return true; } catch (SQLException | StorageQueryException e) { - if (e.getMessage().contains("relation") && e.getMessage().contains(tableName) && e.getMessage().contains("does not exist")) { + if (e.getMessage().contains("relation") && e.getMessage().contains(tableName) && + e.getMessage().contains("does not exist")) { return false; } throw e; @@ -89,7 +91,8 @@ static String getQueryToCreateUsersTable(Start start) { + " 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," + + " 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() + @@ -120,12 +123,16 @@ static String getQueryToCreateUserPaginationIndex2(Start start) { 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);"; + + + "(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);"; + + + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id " + + "DESC);"; } static String getQueryToCreatePrimaryUserId(Start start) { @@ -221,7 +228,8 @@ private static String getQueryToCreateAppIdToUserIdTable(Start start) { + " 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," + + " 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" @@ -614,7 +622,7 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer + getConfig(start).getUserRolesTable() + "," + getConfig(start).getDashboardUsersTable() + "," + getConfig(start).getDashboardSessionsTable() + "," - + getConfig(start).getTotpUsedCodesTable() + "," + + getConfig(start).getTotpUsedCodesTable() + "," + getConfig(start).getTotpUserDevicesTable() + "," + getConfig(start).getTotpUsersTable(); update(start, DROP_QUERY, NO_OP_SETTER); @@ -774,7 +782,8 @@ public static boolean doesUserIdExist(Start start, AppIdentifier appIdentifier, }, ResultSet::next); } - public static boolean doesUserIdExist_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static boolean doesUserIdExist_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { // We query both tables cause there is a case where a primary user ID exists, but its associated // recipe user ID has been deleted AND there are other recipe user IDs linked to this primary user ID already. @@ -986,8 +995,11 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant usersFromQuery = new ArrayList<>(); } else { - String finalQuery = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM ( " + USER_SEARCH_TAG_CONDITION.toString() + " )" - + " AS finalResultTable ORDER BY primary_or_recipe_user_time_joined " + timeJoinedOrder + ", primary_or_recipe_user_id DESC "; + String finalQuery = + "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM ( " + + USER_SEARCH_TAG_CONDITION.toString() + " )" + + " AS finalResultTable ORDER BY primary_or_recipe_user_time_joined " + + timeJoinedOrder + ", primary_or_recipe_user_id DESC "; usersFromQuery = execute(start, finalQuery, pst -> { for (int i = 1; i <= queryList.size(); i++) { pst.setString(i, queryList.get(i - 1)); @@ -1023,9 +1035,12 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant recipeIdCondition = recipeIdCondition + " AND"; } String timeJoinedOrderSymbol = timeJoinedOrder.equals("ASC") ? ">" : "<"; - String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + getConfig(start).getUsersTable() + " WHERE " + String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + + getConfig(start).getUsersTable() + " WHERE " + recipeIdCondition + " (primary_or_recipe_user_time_joined " + timeJoinedOrderSymbol - + " ? OR (primary_or_recipe_user_time_joined = ? AND primary_or_recipe_user_id <= ?)) AND app_id = ? AND tenant_id = ?" + + + " ? OR (primary_or_recipe_user_time_joined = ? AND primary_or_recipe_user_id <= ?)) AND " + + "app_id = ? AND tenant_id = ?" + " ORDER BY primary_or_recipe_user_time_joined " + timeJoinedOrder + ", primary_or_recipe_user_id DESC LIMIT ?"; usersFromQuery = execute(start, QUERY, pst -> { @@ -1051,7 +1066,8 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant }); } else { String recipeIdCondition = RECIPE_ID_CONDITION.toString(); - String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + getConfig(start).getUsersTable() + " WHERE "; + String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + + getConfig(start).getUsersTable() + " WHERE "; if (!recipeIdCondition.equals("")) { QUERY += recipeIdCondition + " AND"; } @@ -1237,7 +1253,8 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction( // now that we have locks on all the relevant tables, we can read from them safely List userIds = ThirdPartyQueries.listUserIdsByThirdPartyInfo_Transaction(start, sqlCon, appIdentifier, thirdPartyId, thirdPartyUserId); - List result = getPrimaryUserInfoForUserIds_Transaction(start, sqlCon, appIdentifier, userIds); + List result = getPrimaryUserInfoForUserIds_Transaction(start, sqlCon, appIdentifier, + userIds); // this is going to order them based on oldest that joined to newest that joined. result.sort(Comparator.comparingLong(o -> o.timeJoined)); @@ -1335,9 +1352,9 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber(Start start, } public static AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(Start start, - TenantIdentifier tenantIdentifier, - String thirdPartyId, - String thirdPartyUserId) + TenantIdentifier tenantIdentifier, + String thirdPartyId, + String thirdPartyUserId) throws StorageQueryException, SQLException { String userId = ThirdPartyQueries.getUserIdByThirdPartyInfo(start, tenantIdentifier, thirdPartyId, thirdPartyUserId); @@ -1371,7 +1388,7 @@ public static AuthRecipeUserInfo getPrimaryUserInfoForUserId(Start start, AppIde } public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String id) + AppIdentifier appIdentifier, String id) throws SQLException, StorageQueryException { List ids = new ArrayList<>(); ids.add(id); @@ -1394,14 +1411,18 @@ private static List getPrimaryUserInfoForUserIds(Start start // which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column, // or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id // column - String QUERY = "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, aaru.tenant_id, aaru.time_joined FROM " + getConfig(start).getAppIdToUserIdTable() + " as au " + - "LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + - " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + - getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" - + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ") OR primary_or_recipe_user_id IN (" + - Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ")) AND app_id = ?) AND au.app_id = ?"; + String QUERY = + "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + + "aaru.tenant_id, aaru.time_joined FROM " + + getConfig(start).getAppIdToUserIdTable() + " as au " + + "LEFT JOIN " + getConfig(start).getUsersTable() + + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + + " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + + getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") OR primary_or_recipe_user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ")) AND app_id = ?) AND au.app_id = ?"; List allAuthUsersResult = execute(start, QUERY, pst -> { // IN user_id @@ -1475,8 +1496,8 @@ private static List getPrimaryUserInfoForUserIds(Start start } private static List getPrimaryUserInfoForUserIds_Transaction(Start start, Connection sqlCon, - AppIdentifier appIdentifier, - List userIds) + AppIdentifier appIdentifier, + List userIds) throws StorageQueryException, SQLException { if (userIds.size() == 0) { return new ArrayList<>(); @@ -1486,14 +1507,18 @@ private static List getPrimaryUserInfoForUserIds_Transaction // which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column, // or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id // column - String QUERY = "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, aaru.tenant_id, aaru.time_joined FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" + - " LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + - " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + - getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" - + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ") OR primary_or_recipe_user_id IN (" + - Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ")) AND app_id = ?) AND au.app_id = ?"; + String QUERY = + "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + + "aaru.tenant_id, aaru.time_joined FROM " + + getConfig(start).getAppIdToUserIdTable() + " as au" + + " LEFT JOIN " + getConfig(start).getUsersTable() + + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + + " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + + getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") OR primary_or_recipe_user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ")) AND app_id = ?) AND au.app_id = ?"; List allAuthUsersResult = execute(sqlCon, QUERY, pst -> { // IN user_id @@ -1530,10 +1555,13 @@ private static List getPrimaryUserInfoForUserIds_Transaction List loginMethods = new ArrayList<>(); loginMethods.addAll( - EmailPasswordQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); - loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); + EmailPasswordQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, + appIdentifier)); + loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, + appIdentifier)); loginMethods.addAll( - PasswordlessQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); + PasswordlessQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, + appIdentifier)); Map recipeUserIdToLoginMethodMap = new HashMap<>(); for (LoginMethod loginMethod : loginMethods) { @@ -1732,20 +1760,20 @@ public static int getUsersCountWithMoreThanOneLoginMethod(Start start, AppIdenti public static int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(Start start, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { String QUERY = - "SELECT COUNT (DISTINCT user_id) as c FROM (" - + " (" // Users with number of login methods > 1 - + " SELECT primary_or_recipe_user_id AS user_id FROM (" - + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" - + " FROM " + getConfig(start).getAppIdToUserIdTable() - + " WHERE app_id = ? " - + " GROUP BY (app_id, primary_or_recipe_user_id)" - + " ) AS nloginmethods" - + " WHERE num_login_methods > 1" - + " ) UNION (" // TOTP users - + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() - + " WHERE app_id = ?" - + " )" - + ") AS all_users"; + "SELECT COUNT (DISTINCT user_id) as c FROM (" + + " (" // Users with number of login methods > 1 + + " SELECT primary_or_recipe_user_id AS user_id FROM (" + + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" + + " FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? " + + " GROUP BY (app_id, primary_or_recipe_user_id)" + + " ) AS nloginmethods" + + " WHERE num_login_methods > 1" + + " ) UNION (" // TOTP users + + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() + + " WHERE app_id = ?" + + " )" + + ") AS all_users"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -1768,7 +1796,8 @@ public static boolean checkIfUsesAccountLinking(Start start, AppIdentifier appId }); } - public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { GeneralQueries.AccountLinkingInfo accountLinkingInfo = new GeneralQueries.AccountLinkingInfo(userId, false); { @@ -1790,7 +1819,8 @@ public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, return accountLinkingInfo; } - public static void updateTimeJoinedForPrimaryUser_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String primaryUserId) + public static void updateTimeJoinedForPrimaryUser_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, String primaryUserId) throws SQLException, StorageQueryException { String QUERY = "UPDATE " + getConfig(start).getUsersTable() + " SET primary_or_recipe_user_time_joined = (SELECT MIN(time_joined) FROM " + 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..9e80fe48 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java @@ -52,14 +52,14 @@ static String getQueryToCreateJWTSigningTable(Start start) { 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," + + "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" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -75,7 +75,7 @@ public static List getJWTSigningKeys_Transaction(Start start, String QUERY = "SELECT * FROM " + getConfig(start).getJWTSigningKeysTable() + " WHERE app_id = ? ORDER BY created_at DESC FOR UPDATE"; - return execute(con, QUERY, pst -> pst.setString(1, appIdentifier.getAppId()),result -> { + return execute(con, QUERY, pst -> pst.setString(1, appIdentifier.getAppId()), result -> { List keys = new ArrayList<>(); while (result.next()) { 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 0d9bf826..d10dde3f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java @@ -51,7 +51,8 @@ static String getQueryToCreateTenantConfigsTable(Start start) { + "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)" + + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") + + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)" + ");"; // @formatter:on } @@ -82,10 +83,12 @@ static String getQueryToCreateTenantThirdPartyProvidersTable(Start start) { + "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, 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" + + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -109,16 +112,20 @@ static String getQueryToCreateTenantThirdPartyProviderClientsTable(Start start) + "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") + + "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" + + " 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);"; + + getConfig(start).getTenantThirdPartyProviderClientsTable() + + " (connection_uri_domain, app_id, tenant_id, third_party_id);"; } public static String getQueryToCreateFirstFactorsTable(Start start) { @@ -134,7 +141,8 @@ public static String getQueryToCreateFirstFactorsTable(Start start) { + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, factor_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "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" + + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -157,14 +165,16 @@ public static String getQueryToCreateRequiredSecondaryFactorsTable(Start start) + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, factor_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "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" + + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE" + ");"; // @formatter:on } public static String getQueryToCreateTenantIdIndexForRequiredSecondaryFactorsTable(Start start) { return "CREATE INDEX IF NOT EXISTS tenant_default_required_factor_ids_tenant_id_index ON " - + getConfig(start).getTenantRequiredSecondaryFactorsTable() + " (connection_uri_domain, app_id, tenant_id);"; + + getConfig(start).getTenantRequiredSecondaryFactorsTable() + + " (connection_uri_domain, app_id, tenant_id);"; } @@ -182,10 +192,12 @@ private static void executeCreateTenantQueries(Start start, Connection sqlCon, T } MfaSqlHelper.createFirstFactors(start, sqlCon, tenantConfig.tenantIdentifier, tenantConfig.firstFactors); - MfaSqlHelper.createRequiredSecondaryFactors(start, sqlCon, tenantConfig.tenantIdentifier, tenantConfig.requiredSecondaryFactors); + MfaSqlHelper.createRequiredSecondaryFactors(start, sqlCon, tenantConfig.tenantIdentifier, + tenantConfig.requiredSecondaryFactors); } - public static void createTenantConfig(Start start, TenantConfig tenantConfig) throws StorageQueryException, StorageTransactionLogicException { + public static void createTenantConfig(Start start, TenantConfig tenantConfig) + throws StorageQueryException, StorageTransactionLogicException { start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); { @@ -201,7 +213,8 @@ public static void createTenantConfig(Start start, TenantConfig tenantConfig) th }); } - public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIdentifier) throws StorageQueryException { + public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIdentifier) + throws StorageQueryException { try { String QUERY = "DELETE FROM " + getConfig(start).getTenantConfigsTable() + " WHERE connection_uri_domain = ? AND app_id = ? AND tenant_id = ?"; @@ -219,7 +232,8 @@ public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIde } } - public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig) throws StorageQueryException, StorageTransactionLogicException { + public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig) + throws StorageQueryException, StorageTransactionLogicException { start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); { @@ -233,7 +247,8 @@ public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig) pst.setString(3, tenantConfig.tenantIdentifier.getTenantId()); }); if (rowsAffected == 0) { - throw new StorageTransactionLogicException(new TenantOrAppNotFoundException(tenantConfig.tenantIdentifier)); + throw new StorageTransactionLogicException( + new TenantOrAppNotFoundException(tenantConfig.tenantIdentifier)); } } @@ -256,16 +271,21 @@ public static TenantConfig[] getAllTenants(Start start) throws StorageQueryExcep try { // Map TenantIdentifier -> thirdPartyId -> clientType - HashMap>> providerClientsMap = ThirdPartyProviderClientSQLHelper.selectAll(start); + HashMap>> providerClientsMap = ThirdPartyProviderClientSQLHelper.selectAll( + start); // Map (tenantIdentifier) -> thirdPartyId -> provider - HashMap> providerMap = ThirdPartyProviderSQLHelper.selectAll(start, providerClientsMap); + HashMap> providerMap = + ThirdPartyProviderSQLHelper.selectAll( + start, providerClientsMap); // Map (tenantIdentifier) -> firstFactors HashMap firstFactorsMap = MfaSqlHelper.selectAllFirstFactors(start); // Map (tenantIdentifier) -> requiredSecondaryFactors - HashMap requiredSecondaryFactorsMap = MfaSqlHelper.selectAllRequiredSecondaryFactors(start); + HashMap requiredSecondaryFactorsMap = + MfaSqlHelper.selectAllRequiredSecondaryFactors( + start); return TenantConfigSQLHelper.selectAll(start, providerMap, firstFactorsMap, requiredSecondaryFactorsMap); } catch (SQLException throwables) { @@ -290,7 +310,7 @@ public static void addTenantIdInTargetStorage(Start start, TenantIdentifier tena } public static void addTenantIdInTargetStorage_Transaction(Start start, Connection con, - TenantIdentifier tenantIdentifier) throws + TenantIdentifier tenantIdentifier) throws SQLException, StorageQueryException { { if (Start.isTesting && simulateErrorInAddingTenantIdInTargetStorage_forTesting) { @@ -314,7 +334,7 @@ public static void addTenantIdInTargetStorage_Transaction(Start start, Connectio + "(app_id, created_at_time)" + " VALUES(?, ?) ON CONFLICT DO NOTHING"; update(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); - pst.setLong(2, currentTime); + pst.setLong(2, currentTime); }); } @@ -325,14 +345,14 @@ public static void addTenantIdInTargetStorage_Transaction(Start start, Connectio update(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); - pst.setLong(3, currentTime); + pst.setLong(3, currentTime); }); } } } public static void deleteTenantIdInTargetStorage(Start start, TenantIdentifier tenantIdentifier) - throws StorageQueryException { + throws StorageQueryException { try { if (tenantIdentifier.getTenantId().equals(TenantIdentifier.DEFAULT_TENANT_ID)) { // Delete the app 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..bfe1a2a9 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java @@ -388,7 +388,8 @@ public static void deleteCode_Transaction(Start start, Connection con, TenantIde }); } - public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenantIdentifier, String id, @Nullable String email, + public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenantIdentifier, String id, + @Nullable String email, @Nullable String phoneNumber, long timeJoined) throws StorageTransactionLogicException, StorageQueryException { return start.startTransaction(con -> { @@ -407,7 +408,9 @@ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenant { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -457,7 +460,7 @@ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenant } private static UserInfoWithTenantId[] getUserInfosWithTenant_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String userId) + AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { String QUERY = "SELECT pl_users.user_id as user_id, pl_users.email as email, " + "pl_users.phone_number as phone_number, pl_users_to_tenant.tenant_id as tenant_id " @@ -742,7 +745,7 @@ public static List getUsersInfoUsingIdList(Start start, Set } public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, - AppIdentifier appIdentifier) + AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { // No need to filter based on tenantId because the id list is already filtered for a tenant @@ -772,7 +775,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start, } private static UserInfoPartial getUserById_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, - String userId) + String userId) throws StorageQueryException, SQLException { // we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id // table @@ -810,8 +813,8 @@ public static List lockEmail_Transaction(Start start, Connection con, Ap } public static List lockPhoneAndTenant_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, - String phoneNumber) + AppIdentifier appIdentifier, + String phoneNumber) throws SQLException, StorageQueryException { String QUERY = "SELECT user_id FROM " + getConfig(start).getPasswordlessUsersTable() + @@ -829,7 +832,7 @@ public static List lockPhoneAndTenant_Transaction(Start start, Connectio } public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, - String email) + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getPasswordlessUserToTenantTable() + " AS pless" + @@ -849,8 +852,9 @@ public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier te }); } - public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - String email) + public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" + @@ -891,8 +895,9 @@ public static String getPrimaryUserByPhoneNumber(Start start, TenantIdentifier t }); } - public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - @Nonnull String phoneNumber) + public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + @Nonnull String phoneNumber) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" + @@ -922,11 +927,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC throw new UnknownUserIdException(); } - GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, + sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " + + "recipe_id, time_joined, primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -939,14 +947,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC pst.setLong(8, userInfo.timeJoined); }); - GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), accountLinkingInfo.primaryUserId); + GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), + accountLinkingInfo.primaryUserId); } { // passwordless_user_to_tenant String QUERY = "INSERT INTO " + getConfig(start).getPasswordlessUserToTenantTable() + "(app_id, tenant_id, user_id, email, phone_number)" + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT " - + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getPasswordlessUserToTenantTable(), null, "pkey") + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), + getConfig(start).getPasswordlessUserToTenantTable(), null, "pkey") + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { 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 8a8269d5..60270a65 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java @@ -32,7 +32,7 @@ public static String getQueryToCreateUsersTable(Start start) { + " 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" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -119,10 +119,13 @@ private static int insertUser_Transaction(Start start, Connection con, AppIdenti }); } - private static int insertDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, TOTPDevice device) + private static int insertDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + TOTPDevice device) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUserDevicesTable() - + " (app_id, user_id, device_name, secret_key, period, skew, verified, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + + + " (app_id, user_id, device_name, secret_key, period, skew, verified, created_at) VALUES (?, ?, ?, ?, " + + "?, ?, ?, ?)"; return update(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -136,13 +139,15 @@ private static int insertDevice_Transaction(Start start, Connection con, AppIden }); } - public static void createDevice_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, TOTPDevice device) + public static void createDevice_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + TOTPDevice device) throws SQLException, StorageQueryException { insertUser_Transaction(start, sqlCon, appIdentifier, device.userId); insertDevice_Transaction(start, sqlCon, appIdentifier, device); } - public static TOTPDevice getDeviceByName_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId, String deviceName) + public static TOTPDevice getDeviceByName_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + String userId, String deviceName) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getTotpUserDevicesTable() + " WHERE app_id = ? AND user_id = ? AND device_name = ? FOR UPDATE;"; @@ -170,7 +175,8 @@ public static int markDeviceAsVerified(Start start, AppIdentifier appIdentifier, }); } - public static int deleteDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String deviceName) + public static int deleteDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, + String deviceName) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + Config.getConfig(start).getTotpUserDevicesTable() + " WHERE app_id = ? AND user_id = ? AND device_name = ?;"; @@ -207,7 +213,8 @@ public static boolean removeUser(Start start, TenantIdentifier tenantIdentifier, return removedUsersCount > 0; } - public static int updateDeviceName(Start start, AppIdentifier appIdentifier, String userId, String oldDeviceName, String newDeviceName) + public static int updateDeviceName(Start start, AppIdentifier appIdentifier, String userId, String oldDeviceName, + String newDeviceName) throws StorageQueryException, SQLException { String QUERY = "UPDATE " + Config.getConfig(start).getTotpUserDevicesTable() + " SET device_name = ? WHERE app_id = ? AND user_id = ? AND device_name = ?;"; @@ -238,7 +245,8 @@ public static TOTPDevice[] getDevices(Start start, AppIdentifier appIdentifier, }); } - public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) + public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String userId) throws StorageQueryException, SQLException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getTotpUserDevicesTable() + " WHERE app_id = ? AND user_id = ? FOR UPDATE;"; @@ -257,10 +265,13 @@ public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, A } - public static int insertUsedCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, TOTPUsedCode code) + public static int insertUsedCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, + TOTPUsedCode code) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUsedCodesTable() - + " (app_id, tenant_id, user_id, code, is_valid, expiry_time_ms, created_time_ms) VALUES (?, ?, ?, ?, ?, ?, ?);"; + + + " (app_id, tenant_id, user_id, code, is_valid, expiry_time_ms, created_time_ms) VALUES (?, ?, ?, ?, " + + "?, ?, ?);"; return update(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -278,7 +289,7 @@ public static int insertUsedCode_Transaction(Start start, Connection con, Tenant * order of creation time. */ public static TOTPUsedCode[] getAllUsedCodesDescOrder_Transaction(Start start, Connection con, - TenantIdentifier tenantIdentifier, String userId) + TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { // Take a lock based on the user id: String QUERY = "SELECT * FROM " + 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..964b53cd 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java @@ -117,7 +117,9 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -274,7 +276,7 @@ public static List getUsersInfoUsingIdList(Start start, Set } public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, - AppIdentifier appIdentifier) + AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined " @@ -305,7 +307,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start, public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifier appIdentifier, - String thirdPartyId, String thirdPartyUserId) + String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " @@ -327,7 +329,8 @@ public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifie }); } - public static List listUserIdsByThirdPartyInfo_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + public static List listUserIdsByThirdPartyInfo_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { @@ -388,7 +391,7 @@ public static void updateUserEmail_Transaction(Start start, Connection con, AppI } private static UserInfoPartial getUserInfoUsingUserId_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String userId) + AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { // we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id @@ -462,11 +465,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC throw new UnknownUserIdException(); } - GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, + sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " + + "recipe_id, time_joined, primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -479,14 +485,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC pst.setLong(8, userInfo.timeJoined); }); - GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), accountLinkingInfo.primaryUserId); + GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), + accountLinkingInfo.primaryUserId); } { // thirdparty_user_to_tenant String QUERY = "INSERT INTO " + getConfig(start).getThirdPartyUserToTenantTable() + "(app_id, tenant_id, user_id, third_party_id, third_party_user_id)" + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT " - + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getThirdPartyUserToTenantTable(), null, "pkey") + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), + getConfig(start).getThirdPartyUserToTenantTable(), null, "pkey") + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); 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 a2388765..072571c1 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java @@ -55,7 +55,8 @@ public static String getQueryToCreateUserIdMappingTable(Start start) { + " 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" + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id, user_id)" + + " ON DELETE CASCADE" + ");"; // @formatter:on } @@ -65,7 +66,8 @@ public static String getQueryToCreateSupertokensUserIdIndexForUserIdMappingTable + getConfig(start).getUserIdMappingTable() + "(app_id, supertokens_user_id);"; } - public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, String superTokensUserId, String externalUserId, + 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() + " (app_id, supertokens_user_id, external_user_id, external_user_id_info)" + " VALUES(?, ?, ?, ?)"; @@ -78,7 +80,8 @@ public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, }); } - public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND supertokens_user_id = ?"; @@ -93,7 +96,8 @@ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, }); } - public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND external_user_id = ?"; @@ -110,7 +114,9 @@ public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppI } public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId(Start start, - AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { + AppIdentifier appIdentifier, + String userId) + throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND (supertokens_user_id = ? OR external_user_id = ?)"; @@ -201,7 +207,8 @@ public static HashMap getUserIdMappingWithUserIds_Transaction(St }); } - public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId) + public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND supertokens_user_id = ?"; @@ -217,7 +224,8 @@ public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppI public static boolean deleteUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { - String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND external_user_id = ?"; + String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() + + " WHERE app_id = ? AND external_user_id = ?"; // store the number of rows updated int rowUpdatedCount = update(start, QUERY, pst -> { @@ -229,8 +237,10 @@ public static boolean deleteUserIdMappingWithExternalUserId(Start start, AppIden } public static boolean updateOrDeleteExternalUserIdInfoWithSuperTokensUserId(Start start, - AppIdentifier appIdentifier, String userId, - @Nullable String externalUserIdInfo) throws SQLException, StorageQueryException { + AppIdentifier appIdentifier, + String userId, + @Nullable String externalUserIdInfo) + throws SQLException, StorageQueryException { String QUERY = "UPDATE " + Config.getConfig(start).getUserIdMappingTable() + " SET external_user_id_info = ? WHERE app_id = ? AND supertokens_user_id = ?"; @@ -245,7 +255,8 @@ public static boolean updateOrDeleteExternalUserIdInfoWithSuperTokensUserId(Star public static boolean updateOrDeleteExternalUserIdInfoWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId, - @Nullable String externalUserIdInfo) throws SQLException, StorageQueryException { + @Nullable String externalUserIdInfo) + throws SQLException, StorageQueryException { String QUERY = "UPDATE " + Config.getConfig(start).getUserIdMappingTable() + " SET external_user_id_info = ? WHERE app_id = ? AND external_user_id = ?"; @@ -258,7 +269,9 @@ public static boolean updateOrDeleteExternalUserIdInfoWithExternalUserId(Start s return rowUpdated > 0; } - public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND supertokens_user_id = ?"; @@ -273,7 +286,9 @@ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(S }); } - public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND external_user_id = ?"; @@ -289,8 +304,10 @@ public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start }); } - public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(Start start, Connection sqlCon, - AppIdentifier appIdentifier, String userId) + public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(Start start, + Connection sqlCon, + AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND (supertokens_user_id = ? OR external_user_id = ?)"; 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 10fcb1a7..5825164c 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java @@ -142,7 +142,7 @@ public static void addPermissionToRoleOrDoNothingIfExists_Transaction(Start star } public static boolean deleteRole(Start start, AppIdentifier appIdentifier, - String role) + String role) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getRolesTable() + " WHERE app_id = ? AND role = ? ;"; diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java index b5abf91d..ccbab4f6 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java @@ -33,11 +33,13 @@ public static HashMap selectAllFirstFactors(Start st throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, factor_id FROM " + getConfig(start).getTenantFirstFactorsTable() + ";"; - return execute(start, QUERY, pst -> {}, result -> { + return execute(start, QUERY, pst -> { + }, result -> { HashMap> firstFactors = new HashMap<>(); while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); if (!firstFactors.containsKey(tenantIdentifier)) { firstFactors.put(tenantIdentifier, new ArrayList<>()); } @@ -58,7 +60,8 @@ public static HashMap selectAllRequiredSecondaryFact throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, factor_id FROM " + getConfig(start).getTenantRequiredSecondaryFactorsTable() + ";"; - return execute(start, QUERY, pst -> {}, result -> { + return execute(start, QUERY, pst -> { + }, result -> { HashMap> defaultRequiredFactors = new HashMap<>(); while (result.next()) { @@ -80,16 +83,18 @@ public static HashMap selectAllRequiredSecondaryFact }); } - public static void createFirstFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String[] firstFactors) + public static void createFirstFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, + String[] firstFactors) throws SQLException, StorageQueryException { if (firstFactors == null || firstFactors.length == 0) { return; } - String QUERY = "INSERT INTO " + getConfig(start).getTenantFirstFactorsTable() + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; + String QUERY = "INSERT INTO " + getConfig(start).getTenantFirstFactorsTable() + + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; for (String factorId : new HashSet<>(Arrays.asList(firstFactors))) { update(sqlCon, QUERY, pst -> { - pst.setString(1, tenantIdentifier.getConnectionUriDomain()); + pst.setString(1, tenantIdentifier.getConnectionUriDomain()); pst.setString(2, tenantIdentifier.getAppId()); pst.setString(3, tenantIdentifier.getTenantId()); pst.setString(4, factorId); @@ -97,13 +102,15 @@ public static void createFirstFactors(Start start, Connection sqlCon, TenantIden } } - public static void createRequiredSecondaryFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String[] requiredSecondaryFactors) + public static void createRequiredSecondaryFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, + String[] requiredSecondaryFactors) throws SQLException, StorageQueryException { if (requiredSecondaryFactors == null || requiredSecondaryFactors.length == 0) { return; } - String QUERY = "INSERT INTO " + getConfig(start).getTenantRequiredSecondaryFactorsTable() + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; + String QUERY = "INSERT INTO " + getConfig(start).getTenantRequiredSecondaryFactorsTable() + + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; for (String factorId : requiredSecondaryFactors) { update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getConnectionUriDomain()); diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java index 1a2e00b2..6d534d12 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java @@ -43,13 +43,16 @@ public static class TenantConfigRowMapper implements RowMapper> providerMap, HashMap firstFactorsMap, HashMap requiredSecondaryFactorsMap) + public static TenantConfig[] selectAll(Start start, + HashMap> providerMap, + HashMap firstFactorsMap, + HashMap requiredSecondaryFactorsMap) throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, core_config," + " email_password_enabled, passwordless_enabled, third_party_enabled FROM " + getConfig(start).getTenantConfigsTable() + ";"; - TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> {}, result -> { + TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> { + }, result -> { List temp = new ArrayList<>(); while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); ThirdPartyConfig.Provider[] providers = null; if (providerMap.containsKey(tenantIdentifier)) { providers = providerMap.get(tenantIdentifier).values().toArray(new ThirdPartyConfig.Provider[0]); } - String[] firstFactors = firstFactorsMap.containsKey(tenantIdentifier) ? firstFactorsMap.get(tenantIdentifier) : new String[0]; + String[] firstFactors = + firstFactorsMap.containsKey(tenantIdentifier) ? firstFactorsMap.get(tenantIdentifier) : + new String[0]; - String[] requiredSecondaryFactors = requiredSecondaryFactorsMap.containsKey(tenantIdentifier) ? requiredSecondaryFactorsMap.get(tenantIdentifier) : new String[0]; + String[] requiredSecondaryFactors = requiredSecondaryFactorsMap.containsKey(tenantIdentifier) ? + requiredSecondaryFactorsMap.get(tenantIdentifier) : new String[0]; - temp.add(TenantConfigSQLHelper.TenantConfigRowMapper.getInstance(providers, firstFactors, requiredSecondaryFactors).mapOrThrow(result)); + temp.add(TenantConfigSQLHelper.TenantConfigRowMapper.getInstance(providers, firstFactors, + requiredSecondaryFactors).mapOrThrow(result)); } TenantConfig[] finalResult = new TenantConfig[temp.size()]; for (int i = 0; i < temp.size(); i++) { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java index ced73d6e..100194ea 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java @@ -35,7 +35,8 @@ public class ThirdPartyProviderClientSQLHelper { public static class TenantThirdPartyProviderClientRowMapper implements RowMapper { - public static final ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper INSTANCE = new ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper(); + public static final ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper INSTANCE = + new ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper(); private TenantThirdPartyProviderClientRowMapper() { } @@ -77,37 +78,48 @@ public ThirdPartyConfig.ProviderClient map(ResultSet result) throws StorageQuery } } - public static HashMap>> selectAll(Start start) + public static HashMap>> selectAll( + Start start) throws SQLException, StorageQueryException { HashMap>> providerClientsMap = new HashMap<>(); - String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, scope, force_pkce, additional_config FROM " - + getConfig(start).getTenantThirdPartyProviderClientsTable() + ";"; + String QUERY = + "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, " + + "client_secret, scope, force_pkce, additional_config FROM " + + getConfig(start).getTenantThirdPartyProviderClientsTable() + ";"; - execute(start, QUERY, pst -> {}, result -> { + execute(start, QUERY, pst -> { + }, result -> { while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); - ThirdPartyConfig.ProviderClient providerClient = ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper.getInstance().mapOrThrow(result); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); + ThirdPartyConfig.ProviderClient providerClient = + ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper.getInstance() + .mapOrThrow(result); if (!providerClientsMap.containsKey(tenantIdentifier)) { providerClientsMap.put(tenantIdentifier, new HashMap<>()); } - if(!providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { + if (!providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { providerClientsMap.get(tenantIdentifier).put(result.getString("third_party_id"), new HashMap<>()); } - providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).put(providerClient.clientType, providerClient); + providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")) + .put(providerClient.clientType, providerClient); } return null; }); return providerClientsMap; } - public static void create(Start start, Connection sqlCon, TenantConfig tenantConfig, ThirdPartyConfig.Provider provider, ThirdPartyConfig.ProviderClient providerClient) + public static void create(Start start, Connection sqlCon, TenantConfig tenantConfig, + ThirdPartyConfig.Provider provider, ThirdPartyConfig.ProviderClient providerClient) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getTenantThirdPartyProviderClientsTable() - + "(connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, scope, force_pkce, additional_config)" + + + "(connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, " + + "scope, force_pkce, additional_config)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; Array scopeArray; diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java index 3f2b6645..321f80d5 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java @@ -40,7 +40,8 @@ private TenantThirdPartyProviderRowMapper(ThirdPartyConfig.ProviderClient[] clie this.clients = clients; } - public static ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper getInstance(ThirdPartyConfig.ProviderClient[] clients) { + public static ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper getInstance( + ThirdPartyConfig.ProviderClient[] clients) { return new ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper(clients); } @@ -82,21 +83,36 @@ public ThirdPartyConfig.Provider map(ResultSet result) throws StorageQueryExcept } } - public static HashMap> selectAll(Start start, HashMap>> providerClientsMap) + public static HashMap> selectAll(Start start, + HashMap>> providerClientsMap) throws SQLException, StorageQueryException { HashMap> providerMap = new HashMap<>(); - String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, name, authorization_endpoint, authorization_endpoint_query_params, token_endpoint, token_endpoint_body_params, user_info_endpoint, user_info_endpoint_query_params, user_info_endpoint_headers, jwks_uri, oidc_discovery_endpoint, require_email, user_info_map_from_id_token_payload_user_id, user_info_map_from_id_token_payload_email, user_info_map_from_id_token_payload_email_verified, user_info_map_from_user_info_endpoint_user_id, user_info_map_from_user_info_endpoint_email, user_info_map_from_user_info_endpoint_email_verified FROM " - + getConfig(start).getTenantThirdPartyProvidersTable() + ";"; + String QUERY = + "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, name, authorization_endpoint, " + + "authorization_endpoint_query_params, token_endpoint, token_endpoint_body_params, " + + "user_info_endpoint, user_info_endpoint_query_params, user_info_endpoint_headers, jwks_uri, " + + "oidc_discovery_endpoint, require_email, user_info_map_from_id_token_payload_user_id, " + + "user_info_map_from_id_token_payload_email, " + + "user_info_map_from_id_token_payload_email_verified, " + + "user_info_map_from_user_info_endpoint_user_id, user_info_map_from_user_info_endpoint_email, " + + "user_info_map_from_user_info_endpoint_email_verified FROM " + + getConfig(start).getTenantThirdPartyProvidersTable() + ";"; - execute(start, QUERY, pst -> {}, result -> { + execute(start, QUERY, pst -> { + }, result -> { while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); ThirdPartyConfig.ProviderClient[] clients = null; - if (providerClientsMap.containsKey(tenantIdentifier) && providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { - clients = providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).values().toArray(new ThirdPartyConfig.ProviderClient[0]); + if (providerClientsMap.containsKey(tenantIdentifier) && + providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { + clients = providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).values() + .toArray(new ThirdPartyConfig.ProviderClient[0]); } - ThirdPartyConfig.Provider provider = ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper.getInstance(clients).mapOrThrow(result); + ThirdPartyConfig.Provider provider = + ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper.getInstance( + clients).mapOrThrow(result); if (!providerMap.containsKey(tenantIdentifier)) { providerMap.put(tenantIdentifier, new HashMap<>()); @@ -108,10 +124,19 @@ public static HashMap { pst.setString(1, tenantConfig.tenantIdentifier.getConnectionUriDomain()); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java b/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java index 6891f739..755b6720 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java @@ -662,9 +662,12 @@ public void testAllConfigsHaveAnAnnotation() throws Exception { continue; } - if (!(field.isAnnotationPresent(UserPoolProperty.class) || field.isAnnotationPresent(ConnectionPoolProperty.class) || field.isAnnotationPresent( + if (!(field.isAnnotationPresent(UserPoolProperty.class) || + field.isAnnotationPresent(ConnectionPoolProperty.class) || field.isAnnotationPresent( NotConflictingWithinUserPool.class))) { - fail(field.getName() + " does not have UserPoolProperty, ConnectionPoolProperty or NotConflictingWithinUserPool annotation"); + fail(field.getName() + + " does not have UserPoolProperty, ConnectionPoolProperty or NotConflictingWithinUserPool " + + "annotation"); } } } diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java index 470e6ce0..2a8aa753 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java @@ -154,14 +154,15 @@ public void testDownTimeWhenChangingConnectionPoolSize() throws Exception { try { TenantIdentifier t1 = new TenantIdentifier(null, null, "t1"); Storage t1Storage = (StorageLayer.getStorage(t1, process.getProcess())); - ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid"+ finalI, "user" + + ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid" + finalI, "user" + finalI + "@example.com"); if (firstErrorTime.get() != -1 && successAfterErrorTime.get() == -1) { successAfterErrorTime.set(System.currentTimeMillis()); } } catch (StorageQueryException e) { - if (e.getMessage().contains("Connection is closed") || e.getMessage().contains("has been closed")) { + if (e.getMessage().contains("Connection is closed") || + e.getMessage().contains("has been closed")) { if (firstErrorTime.get() == -1) { firstErrorTime.set(System.currentTimeMillis()); } @@ -355,7 +356,7 @@ public void testIdleConnectionTimeout() throws Exception { try { TenantIdentifier t1 = new TenantIdentifier(null, null, "t1"); Storage t1Storage = (StorageLayer.getStorage(t1, process.getProcess())); - ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid"+ finalI, "user" + + ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid" + finalI, "user" + finalI + "@example.com"); } catch (StorageQueryException e) { diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java index 22fe29bb..f86faadf 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java @@ -263,9 +263,11 @@ public void testCodeCreationRapidlyWithDifferentEmails() throws Exception { System.out.println("Durations: " + durations.toString()); assertNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); assertNotNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); assert (pass.get()); @@ -627,7 +629,8 @@ public void testLinkAccountsInParallel() throws Exception { for (int i = 0; i < 3000; i++) { es.execute(() -> { try { - AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), user1.getSupertokensUserId()); + AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), + user1.getSupertokensUserId()); AuthRecipe.unlinkAccounts(process.getProcess(), user2.getSupertokensUserId()); } catch (Exception e) { if (e.getMessage().toLowerCase().contains("the transaction might succeed if retried")) { @@ -642,9 +645,11 @@ public void testLinkAccountsInParallel() throws Exception { assert (pass.get()); assertNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); assertNotNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); @@ -685,9 +690,11 @@ public void testCreatePrimaryInParallel() throws Exception { assert (pass.get()); assertNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); assertNotNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java b/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java index 996ed4d2..e675c765 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java @@ -52,7 +52,7 @@ public void beforeEach() { public void testLogLevelNoneOutput() throws Exception { { Utils.setValueInConfig("log_level", "NONE"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -96,7 +96,7 @@ public void testLogLevelNoneOutput() throws Exception { public void testLogLevelErrorOutput() throws Exception { { Utils.setValueInConfig("log_level", "ERROR"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -150,7 +150,7 @@ public void testLogLevelErrorOutput() throws Exception { public void testLogLevelWarnOutput() throws Exception { { Utils.setValueInConfig("log_level", "WARN"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -204,7 +204,7 @@ public void testLogLevelWarnOutput() throws Exception { public void testLogLevelInfoOutput() throws Exception { { Utils.setValueInConfig("log_level", "INFO"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -258,7 +258,7 @@ public void testLogLevelInfoOutput() throws Exception { public void testLogLevelDebugOutput() throws Exception { { Utils.setValueInConfig("log_level", "DEBUG"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java index 591a4ac0..05b85e4d 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java @@ -65,7 +65,7 @@ public void beforeEach() { @Test public void defaultLogging() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; StorageLayer.close(); TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); @@ -109,7 +109,7 @@ public void defaultLogging() throws Exception { @Test public void customLogging() throws Exception { try { - String[] args = { "../" }; + String[] args = {"../"}; Utils.setValueInConfig("info_log_path", "\"tempLogging/info.log\""); Utils.setValueInConfig("error_log_path", "\"tempLogging/error.log\""); @@ -160,7 +160,7 @@ public void customLogging() throws Exception { @Test public void confirmLoggerClosed() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; StorageLayer.close(); TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); @@ -188,7 +188,7 @@ public void confirmLoggerClosed() throws Exception { @Test public void testStandardOutLoggingWithNullStr() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; ByteArrayOutputStream stdOutput = new ByteArrayOutputStream(); ByteArrayOutputStream errorOutput = new ByteArrayOutputStream(); @@ -224,7 +224,7 @@ public void testStandardOutLoggingWithNullStr() throws Exception { @Test public void testStandardOutLoggingWithNull() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; ByteArrayOutputStream stdOutput = new ByteArrayOutputStream(); ByteArrayOutputStream errorOutput = new ByteArrayOutputStream(); @@ -261,7 +261,7 @@ public void testStandardOutLoggingWithNull() throws Exception { @Test public void confirmHikariLoggerClosedOnlyWhenProcessEnds() throws Exception { StorageLayer.close(); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false); FeatureFlagTestContent.getInstance(process.getProcess()) .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY}); @@ -315,7 +315,7 @@ public void confirmHikariLoggerClosedOnlyWhenProcessEnds() throws Exception { @Test public void testDBPasswordMaskingOnDBConnectionFailUsingConnectionUri() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; String dbUser = "db_user"; String dbPassword = "db_password"; @@ -347,7 +347,7 @@ public void testDBPasswordMaskingOnDBConnectionFailUsingConnectionUri() throws E @Test public void testDBPasswordMaskingOnDBConnectionFailUsingCredentials() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; String dbUser = "db_user"; String dbPassword = "db_password"; @@ -381,7 +381,7 @@ public void testDBPasswordMaskingOnDBConnectionFailUsingCredentials() throws Exc @Test public void testDBPasswordMasking() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; ByteArrayOutputStream stdOutput = new ByteArrayOutputStream(); ByteArrayOutputStream errorOutput = new ByteArrayOutputStream(); @@ -416,7 +416,7 @@ public void testDBPasswordMasking() throws Exception { @Test public void testDBPasswordIsNotLoggedWhenProcessStartsEnds() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; Utils.setValueInConfig("error_log_path", "null"); Utils.setValueInConfig("info_log_path", "null"); @@ -479,7 +479,7 @@ public void testDBPasswordIsNotLoggedWhenProcessStartsEnds() throws Exception { @Test public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; Utils.setValueInConfig("error_log_path", "null"); Utils.setValueInConfig("info_log_path", "null"); @@ -505,8 +505,8 @@ public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception { Main main = process.getProcess(); FeatureFlagTestContent.getInstance(main) - .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[] { - EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY }); + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); JsonObject config = new JsonObject(); TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null); @@ -548,8 +548,8 @@ public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception { Main main = process.getProcess(); FeatureFlagTestContent.getInstance(main) - .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[] { - EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY }); + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null); JsonObject config = new JsonObject(); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java index 37346a11..5f55bcf1 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java @@ -107,7 +107,8 @@ private void createEmailPasswordUsers(Main main) throws Exception { String userId = io.supertokens.utils.Utils.getUUID(); long timeJoined = System.currentTimeMillis(); - storage.signUp(TenantIdentifier.BASE_TENANT, userId, "eptest" + finalI + "@example.com", combinedPasswordHash, + storage.signUp(TenantIdentifier.BASE_TENANT, userId, "eptest" + finalI + "@example.com", + combinedPasswordHash, timeJoined); synchronized (lock) { allUserIds.add(userId); @@ -116,7 +117,7 @@ private void createEmailPasswordUsers(Main main) throws Exception { throw new RuntimeException(e); } if (finalI % 10000 == 9999) { - System.out.println("Created " + ((finalI +1)) + " users"); + System.out.println("Created " + ((finalI + 1)) + " users"); } }); } @@ -137,7 +138,8 @@ private void createPasswordlessUsersWithEmail(Main main) throws Exception { String userId = io.supertokens.utils.Utils.getUUID(); long timeJoined = System.currentTimeMillis(); try { - storage.createUser(TenantIdentifier.BASE_TENANT, userId, "pltest" + finalI + "@example.com", null, timeJoined); + storage.createUser(TenantIdentifier.BASE_TENANT, userId, "pltest" + finalI + "@example.com", null, + timeJoined); synchronized (lock) { allUserIds.add(userId); } @@ -146,7 +148,7 @@ private void createPasswordlessUsersWithEmail(Main main) throws Exception { } if (finalI % 10000 == 9999) { - System.out.println("Created " + ((finalI +1)) + " users"); + System.out.println("Created " + ((finalI + 1)) + " users"); } }); } @@ -176,7 +178,7 @@ private void createPasswordlessUsersWithPhone(Main main) throws Exception { } if (finalI % 10000 == 9999) { - System.out.println("Created " + ((finalI +1)) + " users"); + System.out.println("Created " + ((finalI + 1)) + " users"); } }); } @@ -198,7 +200,8 @@ private void createThirdpartyUsers(Main main) throws Exception { long timeJoined = System.currentTimeMillis(); try { - storage.signUp(TenantIdentifier.BASE_TENANT, userId, "tptest" + finalI + "@example.com", new LoginMethod.ThirdParty("google", "googleid" + finalI), timeJoined ); + storage.signUp(TenantIdentifier.BASE_TENANT, userId, "tptest" + finalI + "@example.com", + new LoginMethod.ThirdParty("google", "googleid" + finalI), timeJoined); synchronized (lock) { allUserIds.add(userId); } @@ -207,7 +210,7 @@ private void createThirdpartyUsers(Main main) throws Exception { } if (finalI % 10000 == 9999) { - System.out.println("Created " + (finalI +1) + " users"); + System.out.println("Created " + (finalI + 1) + " users"); } }); } @@ -646,7 +649,8 @@ private void sanityCheckAPIs(Main main) throws Exception { JsonArray userRolesArr = response.getAsJsonArray("roles"); assertEquals(1, userRolesArr.size()); assertTrue( - userRolesArr.get(0).getAsString().equals("admin") || userRolesArr.get(0).getAsString().equals("user") + userRolesArr.get(0).getAsString().equals("admin") || + userRolesArr.get(0).getAsString().equals("user") ); } @@ -791,7 +795,8 @@ private void measureOperations(Main main) throws Exception { int finalI = i; es.execute(() -> { try { - ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, "twitter" + finalI + "@example.com"); + ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, + "twitter" + finalI + "@example.com"); } catch (Exception e) { errorCount.incrementAndGet(); throw new RuntimeException(e); @@ -817,7 +822,8 @@ private void measureOperations(Main main) throws Exception { int finalI = i; es.execute(() -> { try { - ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, "twitter" + finalI + "@example.com"); + ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, + "twitter" + finalI + "@example.com"); } catch (Exception e) { errorCount.incrementAndGet(); throw new RuntimeException(e); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java index 8f6d1699..e2ee74f2 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java @@ -115,13 +115,18 @@ public void testLinkedAccountUser() throws Exception { AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "test1@example.com", "password"); Thread.sleep(50); - AuthRecipeUserInfo user2 = ThirdParty.signInUp(process.getProcess(), "google", "googleid", "test2@example.com").user; + AuthRecipeUserInfo user2 = ThirdParty.signInUp(process.getProcess(), "google", "googleid", + "test2@example.com").user; Thread.sleep(50); - Passwordless.CreateCodeResponse code1 = Passwordless.createCode(process.getProcess(), "test3@example.com", null, null, null); - AuthRecipeUserInfo user3 = Passwordless.consumeCode(process.getProcess(), code1.deviceId, code1.deviceIdHash, code1.userInputCode, null).user; + Passwordless.CreateCodeResponse code1 = Passwordless.createCode(process.getProcess(), "test3@example.com", null, + null, null); + AuthRecipeUserInfo user3 = Passwordless.consumeCode(process.getProcess(), code1.deviceId, code1.deviceIdHash, + code1.userInputCode, null).user; Thread.sleep(50); - Passwordless.CreateCodeResponse code2 = Passwordless.createCode(process.getProcess(), null, "+919876543210", null, null); - AuthRecipeUserInfo user4 = Passwordless.consumeCode(process.getProcess(), code2.deviceId, code2.deviceIdHash, code2.userInputCode, null).user; + Passwordless.CreateCodeResponse code2 = Passwordless.createCode(process.getProcess(), null, "+919876543210", + null, null); + AuthRecipeUserInfo user4 = Passwordless.consumeCode(process.getProcess(), code2.deviceId, code2.deviceIdHash, + code2.userInputCode, null).user; AuthRecipe.createPrimaryUser(process.getProcess(), user3.getSupertokensUserId()); AuthRecipe.linkAccounts(process.getProcess(), user1.getSupertokensUserId(), user3.getSupertokensUserId()); @@ -135,8 +140,9 @@ public void testLinkedAccountUser() throws Exception { user4.getSupertokensUserId() }; - for (String userId : userIds){ - AuthRecipeUserInfo primaryUser = ((AuthRecipeStorage) StorageLayer.getStorage(process.getProcess())).getPrimaryUserById( + for (String userId : userIds) { + AuthRecipeUserInfo primaryUser = ((AuthRecipeStorage) StorageLayer.getStorage( + process.getProcess())).getPrimaryUserById( new AppIdentifier(null, null), userId); assertEquals(user3.getSupertokensUserId(), primaryUser.getSupertokensUserId()); assertEquals(4, primaryUser.loginMethods.length); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java b/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java index 51673061..eb135240 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java @@ -86,11 +86,12 @@ public void testThatTenantCannotSetDatabaseRelatedConfigIfSuperTokensSaaSSecretI try { JsonObject j = new JsonObject(); j.addProperty(PROTECTED_DB_CONFIG[i], ""); - Multitenancy.addNewOrUpdateAppOrTenant(process.main, new TenantConfig(new TenantIdentifier(null, null, "t1"), new EmailPasswordConfig(false), - new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), - new PasswordlessConfig(false), - null, null, - j), true); + Multitenancy.addNewOrUpdateAppOrTenant(process.main, + new TenantConfig(new TenantIdentifier(null, null, "t1"), new EmailPasswordConfig(false), + new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), + new PasswordlessConfig(false), + null, null, + j), true); fail(); } catch (BadPermissionException e) { assertEquals(e.getMessage(), "Not allowed to modify DB related configs."); @@ -162,12 +163,13 @@ public void testThatTenantCanSetDatabaseRelatedConfigIfSuperTokensSaaSSecretIsNo } else if (PROTECTED_DB_CONFIG_VALUES[i] instanceof Integer) { j.addProperty(PROTECTED_DB_CONFIG[i], (Integer) PROTECTED_DB_CONFIG_VALUES[i]); } - Multitenancy.addNewOrUpdateAppOrTenant(process.main, new TenantConfig(new TenantIdentifier(null, null, "t1"), - new EmailPasswordConfig(false), - new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), - new PasswordlessConfig(false), - null, null, - j), false); + Multitenancy.addNewOrUpdateAppOrTenant(process.main, + new TenantConfig(new TenantIdentifier(null, null, "t1"), + new EmailPasswordConfig(false), + new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), + new PasswordlessConfig(false), + null, null, + j), false); } JsonObject coreConfig = new JsonObject(); @@ -224,7 +226,8 @@ public void testThatTenantCannotGetDatabaseRelatedConfigIfSuperTokensSaaSSecretI { JsonObject response = HttpRequestForTesting.sendJsonRequest(process.getProcess(), "", - HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, "/recipe/multitenancy/tenant/list"), + HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, + "/recipe/multitenancy/tenant/list"), null, 1000, 1000, null, SemVer.v3_0.get(), "GET", apiKey, "multitenancy"); @@ -245,7 +248,8 @@ public void testThatTenantCannotGetDatabaseRelatedConfigIfSuperTokensSaaSSecretI { JsonObject response = HttpRequestForTesting.sendJsonRequest(process.getProcess(), "", - HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, "/recipe/multitenancy/tenant/list"), + HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, + "/recipe/multitenancy/tenant/list"), null, 1000, 1000, null, SemVer.v3_0.get(), "GET", saasSecret, "multitenancy"); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java b/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java index f215a4c0..81f989c8 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java @@ -45,7 +45,7 @@ public void beforeEach() { @Test public void checkingCreationOfNewTable() throws InterruptedException { - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java b/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java index 5dec4454..4c3f6e25 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java @@ -75,8 +75,9 @@ public void testThatMainThreadIsSameThroughout() throws Exception { JsonObject requestBody = new JsonObject(); requestBody.addProperty("appId", "a1"); requestBody.add("coreConfig", config); - HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", "http://localhost:3567/recipe/multitenancy/app", - requestBody, 1000, 2500, null, "3.0", "multitenancy"); + HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/multitenancy/app", + requestBody, 1000, 2500, null, "3.0", "multitenancy"); Storage storage2 = StorageLayer.getStorage(new TenantIdentifier(null, "a1", null), process.getProcess()); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java b/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java index 5ca0aa3e..52e1f7cf 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java @@ -62,7 +62,8 @@ private static boolean isJsonValid(String jsonInString) { @SuppressWarnings("unchecked") public static T sendGETRequest(Main main, String requestID, String url, Map params, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, + String rid) throws IOException, HttpResponseException { StringBuilder paramBuilder = new StringBuilder(); @@ -198,101 +199,107 @@ public static T sendJsonRequest(Main main, String requestID, String url, Jso } public static T sendJsonPOSTRequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "POST", null, rid); } public static T sendJsonPOSTRequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String apiKey, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String apiKey, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "POST", apiKey, rid); } public static T sendJsonPUTRequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "PUT", null, rid); } public static T sendJsonDELETERequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "DELETE", null, rid); } @SuppressWarnings("unchecked") - public static T sendJsonDELETERequestWithQueryParams(Main main, String requestID, String url, Map params, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + public static T sendJsonDELETERequestWithQueryParams(Main main, String requestID, String url, + Map params, + int connectionTimeoutMS, int readTimeoutMS, + Integer version, String cdiVersion, String rid) throws IOException, HttpResponseException { - StringBuilder paramBuilder = new StringBuilder(); + StringBuilder paramBuilder = new StringBuilder(); - if (params != null) { - for (Map.Entry entry : params.entrySet()) { - paramBuilder.append(entry.getKey()).append("=") - .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("&"); - } - } - String paramsStr = paramBuilder.toString(); - if (!paramsStr.equals("")) { - paramsStr = paramsStr.substring(0, paramsStr.length() - 1); - url = url + "?" + paramsStr; + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + paramBuilder.append(entry.getKey()).append("=") + .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("&"); + } + } + String paramsStr = paramBuilder.toString(); + if (!paramsStr.equals("")) { + paramsStr = paramsStr.substring(0, paramsStr.length() - 1); + url = url + "?" + paramsStr; + } + URL obj = getURL(main, requestID, url); + InputStream inputStream = null; + HttpURLConnection con = null; + + try { + con = (HttpURLConnection) obj.openConnection(); + con.setRequestMethod("DELETE"); + con.setConnectTimeout(connectionTimeoutMS); + con.setReadTimeout(readTimeoutMS + 1000); + if (version != null) { + con.setRequestProperty("api-version", version + ""); + } + if (cdiVersion != null) { + con.setRequestProperty("cdi-version", cdiVersion); + } + if (rid != null) { + con.setRequestProperty("rId", rid); + } + + int responseCode = con.getResponseCode(); + + if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { + inputStream = con.getInputStream(); + } else { + inputStream = con.getErrorStream(); + } + + StringBuilder response = new StringBuilder(); + try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); } - URL obj = getURL(main, requestID, url); - InputStream inputStream = null; - HttpURLConnection con = null; - - try { - con = (HttpURLConnection) obj.openConnection(); - con.setRequestMethod("DELETE"); - con.setConnectTimeout(connectionTimeoutMS); - con.setReadTimeout(readTimeoutMS + 1000); - if (version != null) { - con.setRequestProperty("api-version", version + ""); - } - if (cdiVersion != null) { - con.setRequestProperty("cdi-version", cdiVersion); - } - if (rid != null) { - con.setRequestProperty("rId", rid); - } - - int responseCode = con.getResponseCode(); - - if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { - inputStream = con.getInputStream(); - } else { - inputStream = con.getErrorStream(); - } - - StringBuilder response = new StringBuilder(); - try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - String inputLine; - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - } - if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { - if (!isJsonValid(response.toString())) { - return (T) response.toString(); - } - return (T) (new JsonParser().parse(response.toString())); - } - throw new HttpResponseException(responseCode, response.toString()); - } finally { - if (inputStream != null) { - inputStream.close(); - } - - if (con != null) { - con.disconnect(); - } + } + if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { + if (!isJsonValid(response.toString())) { + return (T) response.toString(); } + return (T) (new JsonParser().parse(response.toString())); + } + throw new HttpResponseException(responseCode, response.toString()); + } finally { + if (inputStream != null) { + inputStream.close(); + } + + if (con != null) { + con.disconnect(); + } } + } public static String getMultitenantUrl(TenantIdentifier tenantIdentifier, String path) { StringBuilder sb = new StringBuilder(); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java index 97a3fb6b..e6b10a6c 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java @@ -329,7 +329,8 @@ public void mergingTenantWithBaseConfigWithConflictingConfigsThrowsError() fail(); } catch (InvalidConfigException e) { assertEquals(e.getMessage(), - "You cannot set different values for postgresql_thirdparty_users_table_name for the same user pool"); + "You cannot set different values for postgresql_thirdparty_users_table_name for the same user " + + "pool"); } process.kill(); @@ -806,7 +807,8 @@ public void testTenantCreationAndThenDbDownDbThrowsErrorInRecipesAndDoesntAffect tenantConfigJson); StorageLayer.getMultitenancyStorage(process.getProcess()).createTenant(tenantConfig); - MultitenancyHelper.getInstance(process.getProcess()).refreshTenantsInCoreBasedOnChangesInCoreConfigOrIfTenantListChanged(true); + MultitenancyHelper.getInstance(process.getProcess()) + .refreshTenantsInCoreBasedOnChangesInCoreConfigOrIfTenantListChanged(true); try { EmailPassword.signIn(tid, (StorageLayer.getStorage(tid, process.getProcess())), diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java index b8635224..801737fd 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java @@ -107,7 +107,8 @@ public void testThatCUDRecoversWhenItFailsToAddEntryDuringCreation() throws Exce assertEquals(2, allTenants.length); // should have the new CUD try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -117,14 +118,16 @@ public void testThatCUDRecoversWhenItFailsToAddEntryDuringCreation() throws Exce MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false; // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } @Test public void testThatCUDRecoversWhenTheDbIsDownDuringCreationButDbComesUpLater() throws Exception { Start start = ((Start) StorageLayer.getBaseStorage(process.getProcess())); try { - update(start, "DROP DATABASE st5000;", pst -> {}); + update(start, "DROP DATABASE st5000;", pst -> { + }); } catch (Exception e) { // ignore } @@ -147,24 +150,30 @@ public void testThatCUDRecoversWhenTheDbIsDownDuringCreationButDbComesUpLater() fail(); } catch (StorageQueryException e) { // ignore - assertEquals("java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage()); + assertEquals( + "java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to " + + "initialize pool: FATAL: database \"st5000\" does not exist", + e.getMessage()); } TenantConfig[] allTenants = MultitenancyHelper.getInstance(process.getProcess()).getAllTenants(); assertEquals(2, allTenants.length); // should have the new CUD try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore assertTrue(e.getMessage().contains("Internal Error")); // db is still down } - update(start, "CREATE DATABASE st5000;", pst -> {}); + update(start, "CREATE DATABASE st5000;", pst -> { + }); // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } @Test @@ -260,7 +269,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddEntry MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false; // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } @Test @@ -291,7 +301,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenTenantEntryIsInconsistentIn assertEquals(2, allTenants.length); // should have the new CUD Start start = (Start) StorageLayer.getBaseStorage(process.getProcess()); - update(start, "DELETE FROM apps;", pst -> {}); + update(start, "DELETE FROM apps;", pst -> { + }); process.kill(false); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); @@ -306,13 +317,15 @@ public void testThatCoreDoesNotCrashDuringStartupWhenTenantEntryIsInconsistentIn MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false; // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); Session.createNewSession(process.getProcess(), "userid", new JsonObject(), new JsonObject()); } @Test - public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntryInTheBaseTenantStorage() throws Exception { + public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntryInTheBaseTenantStorage() + throws Exception { JsonObject coreConfig = new JsonObject(); TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null); @@ -354,7 +367,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntry assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); Session.createNewSession( new TenantIdentifier(null, "a1", null), @@ -364,7 +378,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntry } @Test - public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenantEntryInTargetStorageWithLoadOnlyCUDConfig() throws Exception { + public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenantEntryInTargetStorageWithLoadOnlyCUDConfig() + throws Exception { JsonObject coreConfig = new JsonObject(); TenantIdentifier tenantIdentifier = new TenantIdentifier("127.0.0.1", null, null); @@ -442,10 +457,12 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan assertEquals(2, allTenants.length); // should have the new CUD // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); try { - tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -465,9 +482,11 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan assertEquals(2, allTenants.length); // should have the new CUD // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -488,7 +507,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throws Exception { Start start = ((Start) StorageLayer.getBaseStorage(process.getProcess())); try { - update(start, "DROP DATABASE st5000;", pst -> {}); + update(start, "DROP DATABASE st5000;", pst -> { + }); } catch (Exception e) { // ignore } @@ -511,14 +531,18 @@ public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throw fail(); } catch (StorageQueryException e) { // ignore - assertEquals("java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage()); + assertEquals( + "java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to " + + "initialize pool: FATAL: database \"st5000\" does not exist", + e.getMessage()); } TenantConfig[] allTenants = MultitenancyHelper.getInstance(process.getProcess()).getAllTenants(); assertEquals(2, allTenants.length); // should have the new CUD try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -537,13 +561,16 @@ public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throw // the process should start successfully even though the db is down start = ((Start) StorageLayer.getBaseStorage(process.getProcess())); - update(start, "CREATE DATABASE st5000;", pst -> {}); + update(start, "CREATE DATABASE st5000;", pst -> { + }); // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } - public static JsonObject tpSignInUpAndGetResponse(TenantIdentifier tenantIdentifier, String thirdPartyId, String thirdPartyUserId, String email, Main main, SemVer version) + public static JsonObject tpSignInUpAndGetResponse(TenantIdentifier tenantIdentifier, String thirdPartyId, + String thirdPartyUserId, String email, Main main, SemVer version) throws HttpResponseException, IOException { JsonObject emailObject = new JsonObject(); emailObject.addProperty("id", email); From 729ba6a623992ce67bf2f4f399a3c2024a45cc6b Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 8 Jul 2024 21:41:55 +0530 Subject: [PATCH 04/45] reformates code --- .github/PULL_REQUEST_TEMPLATE.md | 23 +- .github/helpers/package.json | 24 +- .../workflows/github-actions-changelog.yml | 12 +- .github/workflows/lint-pr-title.yml | 30 +- .github/workflows/tests-pass-check-pr.yml | 38 +- .github/workflows/tests.yml | 8 +- CHANGELOG.md | 63 +-- CONTRIBUTING.md | 57 ++- LICENSE.md | 371 +++++++++--------- README.md | 14 +- .../storage/postgresql/ConnectionPool.java | 3 +- .../postgresql/QueryExecutorTemplate.java | 4 +- .../supertokens/storage/postgresql/Start.java | 56 ++- .../postgresql/annotations/DashboardInfo.java | 8 +- .../storage/postgresql/config/Config.java | 3 +- .../postgresql/config/PostgreSQLConfig.java | 101 +++-- .../queries/ActiveUsersQueries.java | 47 +-- .../postgresql/queries/DashboardQueries.java | 19 +- .../queries/EmailPasswordQueries.java | 33 +- .../queries/EmailVerificationQueries.java | 20 +- .../postgresql/queries/GeneralQueries.java | 140 ++++--- .../postgresql/queries/JWTSigningQueries.java | 8 +- .../queries/MultitenancyQueries.java | 64 +-- .../queries/PasswordlessQueries.java | 42 +- .../postgresql/queries/TOTPQueries.java | 33 +- .../postgresql/queries/ThirdPartyQueries.java | 26 +- .../queries/UserIdMappingQueries.java | 45 ++- .../postgresql/queries/UserRolesQueries.java | 2 +- .../queries/multitenancy/MfaSqlHelper.java | 23 +- .../multitenancy/TenantConfigSQLHelper.java | 31 +- .../ThirdPartyProviderClientSQLHelper.java | 34 +- .../ThirdPartyProviderSQLHelper.java | 47 ++- .../storage/postgresql/test/ConfigTest.java | 7 +- .../postgresql/test/DbConnectionPoolTest.java | 7 +- .../storage/postgresql/test/DeadlockTest.java | 21 +- .../storage/postgresql/test/LogLevelTest.java | 10 +- .../storage/postgresql/test/LoggingTest.java | 30 +- .../postgresql/test/OneMillionUsersTest.java | 26 +- .../test/PostgresSQLConfigTest.java | 10 +- .../postgresql/test/StorageLayerTest.java | 20 +- .../test/SuperTokensSaaSSecretTest.java | 30 +- .../postgresql/test/TableCreationTest.java | 2 +- .../postgresql/test/TestMainThread.java | 5 +- .../httpRequest/HttpRequestForTesting.java | 141 +++---- .../test/multitenancy/StorageLayerTest.java | 6 +- .../TestForNoCrashDuringStartup.java | 73 ++-- 46 files changed, 1086 insertions(+), 731 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4de1c9d4..2b22ae85 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,27 +1,38 @@ ## Summary of change + (A few sentences about this PR) ## Related issues + - Link to issue1 here - Link to issue1 here ## Test Plan -(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!) + +(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your +changes work. Bonus points for screenshots and videos!) ## Documentation changes -(If relevant, please create a PR in our [docs repo](https://github.com/supertokens/docs), or create a checklist here highlighting the necessary changes) + +(If relevant, please create a PR in our [docs repo](https://github.com/supertokens/docs), or create a checklist here +highlighting the necessary changes) ## Checklist for important updates + - [ ] Changelog has been updated - [ ] `pluginInterfaceSupported.json` file has been updated (if needed) - [ ] Changes to the version if needed - - In `build.gradle` + - In `build.gradle` - [ ] Had installed and ran the pre-commit hook -- [ ] If there are new dependencies that have been added in `build.gradle`, please make sure to add them in `implementationDependencies.json`. +- [ ] If there are new dependencies that have been added in `build.gradle`, please make sure to add them + in `implementationDependencies.json`. - [ ] Issue this PR against the latest non released version branch. - - To know which one it is, run find the latest released tag (`git tag`) in the format `vX.Y.Z`, and then find the latest branch (`git branch --all`) whose `X.Y` is greater than the latest released tag. - - If no such branch exists, then create one from the latest released branch. + - To know which one it is, run find the latest released tag (`git tag`) in the format `vX.Y.Z`, and then find the + latest branch (`git branch --all`) whose `X.Y` is greater than the latest released tag. + - If no such branch exists, then create one from the latest released branch. - [ ] When adding new recipes, ensure that its performance is being measured in the `OneMillionUsersTest` + ## Remaining TODOs for this PR + - [ ] Item1 - [ ] Item2 \ No newline at end of file diff --git a/.github/helpers/package.json b/.github/helpers/package.json index 80ec546b..28051cdb 100644 --- a/.github/helpers/package.json +++ b/.github/helpers/package.json @@ -1,14 +1,14 @@ { - "name": "helpers", - "version": "1.0.0", - "description": "", - "main": "test-pass-check-pr.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "dependencies": { - "github-workflow-helpers": "github:supertokens/github-workflow-helpers" - } + "name": "helpers", + "version": "1.0.0", + "description": "", + "main": "test-pass-check-pr.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "github-workflow-helpers": "github:supertokens/github-workflow-helpers" + } } \ No newline at end of file diff --git a/.github/workflows/github-actions-changelog.yml b/.github/workflows/github-actions-changelog.yml index 0007ca9a..47aaf4a2 100644 --- a/.github/workflows/github-actions-changelog.yml +++ b/.github/workflows/github-actions-changelog.yml @@ -1,15 +1,15 @@ name: "Enforcing changelog in PRs Workflow" on: pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] + types: [ opened, synchronize, reopened, ready_for_review, labeled, unlabeled ] jobs: # Enforces the update of a changelog file on every pull request changelog: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: dangoslen/changelog-enforcer@v2 - with: - changeLogPath: 'CHANGELOG.md' - skipLabels: 'Skip-Changelog' \ No newline at end of file + - uses: actions/checkout@v2 + - uses: dangoslen/changelog-enforcer@v2 + with: + changeLogPath: 'CHANGELOG.md' + skipLabels: 'Skip-Changelog' \ No newline at end of file diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index 904efde8..96281a74 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -1,20 +1,20 @@ name: "Lint PR Title" on: - pull_request: - types: - - opened - - reopened - - edited - - synchronize + pull_request: + types: + - opened + - reopened + - edited + - synchronize jobs: - pr-title: - name: Lint PR title - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - validateSingleCommit: true \ No newline at end of file + pr-title: + name: Lint PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + validateSingleCommit: true \ No newline at end of file diff --git a/.github/workflows/tests-pass-check-pr.yml b/.github/workflows/tests-pass-check-pr.yml index 4f03705b..07467d9d 100644 --- a/.github/workflows/tests-pass-check-pr.yml +++ b/.github/workflows/tests-pass-check-pr.yml @@ -1,24 +1,24 @@ name: "Check if \"Run tests\" action succeeded" on: - pull_request: - types: - - opened - - reopened - - edited - - synchronize + pull_request: + types: + - opened + - reopened + - edited + - synchronize jobs: - pr-run-test-action: - name: Check if "Run tests" action succeeded - timeout-minutes: 60 - concurrency: - group: ${{ github.head_ref }} - cancel-in-progress: true - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: node install - run: cd ./.github/helpers && npm i - - name: Calling github API - run: cd ./.github/helpers && GITHUB_TOKEN=${{ github.token }} REPO=${{ github.repository }} RUN_ID=${{ github.run_id }} BRANCH=${{ github.head_ref }} JOB_ID=${{ github.job }} SOURCE_OWNER=${{ github.event.pull_request.head.repo.owner.login }} CURRENT_SHA=${{ github.event.pull_request.head.sha }} node node_modules/github-workflow-helpers/test-pass-check-pr.js \ No newline at end of file + pr-run-test-action: + name: Check if "Run tests" action succeeded + timeout-minutes: 60 + concurrency: + group: ${{ github.head_ref }} + cancel-in-progress: true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: node install + run: cd ./.github/helpers && npm i + - name: Calling github API + run: cd ./.github/helpers && GITHUB_TOKEN=${{ github.token }} REPO=${{ github.repository }} RUN_ID=${{ github.run_id }} BRANCH=${{ github.head_ref }} JOB_ID=${{ github.job }} SOURCE_OWNER=${{ github.event.pull_request.head.repo.owner.login }} CURRENT_SHA=${{ github.event.pull_request.head.sha }} node node_modules/github-workflow-helpers/test-pass-check-pr.js \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8bb11ba3..dc2133e9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,19 +4,19 @@ on: inputs: coreRepoOwnerName: description: 'supertokens-core repo owner name' - default: supertokens + default: supertokens required: true coreRepoBranch: description: 'supertokens-core repos branch name' - default: master + default: master required: true pluginRepoOwnerName: description: 'supertokens-plugin-interface repo owner name' - default: supertokens + default: supertokens required: true pluginInterfaceBranch: description: 'supertokens-plugin-interface repos branch name' - default: master + default: master required: true jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 518be7b1..ffb3c2ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [7.1.0] - Adds implementation for a new method `getConfigFieldsInfo` to fetch the plugin config fields. -- Adds `null` state for `firstFactors` and `providers` by adding `is_first_factors_null` and `is_third_party_providers_null` fields in `tenant_configs` table +- Adds `null` state for `firstFactors` and `providers` by adding `is_first_factors_null` + and `is_third_party_providers_null` fields in `tenant_configs` table ### Migration @@ -32,8 +33,8 @@ ALTER TABLE tenant_configs ALTER COLUMN is_third_party_providers_null DROP DEFAU - Support for MFA recipe - Adds `firstFactors` and `requiredSecondaryFactors` for tenant config. - Adds a new `useStaticKey` param to `updateSessionInfo_Transaction` - - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to - change the signing key type of a session + - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to + change the signing key type of a session ### Migration @@ -64,7 +65,8 @@ ALTER TABLE user_roles DROP CONSTRAINT IF EXISTS user_roles_role_fkey; - Fixes the issue where passwords were inadvertently logged in the logs. - Adds tests to check connection pool behaviour. -- Adds `postgresql_idle_connection_timeout` and `postgresql_minimum_idle_connections` configs to control active connections to the database. +- Adds `postgresql_idle_connection_timeout` and `postgresql_minimum_idle_connections` configs to control active + connections to the database. ## [5.0.6] - 2023-12-05 @@ -104,15 +106,16 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ ### Changes - Support for Account Linking - - Adds columns `primary_or_recipe_user_id`, `is_linked_or_is_a_primary_user` and `primary_or_recipe_user_time_joined` to `all_auth_recipe_users` table - - Adds columns `primary_or_recipe_user_id` and `is_linked_or_is_a_primary_user` to `app_id_to_user_id` table - - Removes index `all_auth_recipe_users_pagination_index` and addes `all_auth_recipe_users_pagination_index1`, - `all_auth_recipe_users_pagination_index2`, `all_auth_recipe_users_pagination_index3` and - `all_auth_recipe_users_pagination_index4` indexes instead on `all_auth_recipe_users` table - - Adds `all_auth_recipe_users_recipe_id_index` on `all_auth_recipe_users` table - - Adds `all_auth_recipe_users_primary_user_id_index` on `all_auth_recipe_users` table - - Adds `email` column to `emailpassword_pswd_reset_tokens` table - - Changes `user_id` foreign key constraint on `emailpassword_pswd_reset_tokens` to `app_id_to_user_id` table + - Adds columns `primary_or_recipe_user_id`, `is_linked_or_is_a_primary_user` + and `primary_or_recipe_user_time_joined` to `all_auth_recipe_users` table + - Adds columns `primary_or_recipe_user_id` and `is_linked_or_is_a_primary_user` to `app_id_to_user_id` table + - Removes index `all_auth_recipe_users_pagination_index` and addes `all_auth_recipe_users_pagination_index1`, + `all_auth_recipe_users_pagination_index2`, `all_auth_recipe_users_pagination_index3` and + `all_auth_recipe_users_pagination_index4` indexes instead on `all_auth_recipe_users` table + - Adds `all_auth_recipe_users_recipe_id_index` on `all_auth_recipe_users` table + - Adds `all_auth_recipe_users_primary_user_id_index` on `all_auth_recipe_users` table + - Adds `email` column to `emailpassword_pswd_reset_tokens` table + - Changes `user_id` foreign key constraint on `emailpassword_pswd_reset_tokens` to `app_id_to_user_id` table ### Migration @@ -193,21 +196,20 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ - Fixes null pointer issue when user belongs to no tenant. - ## [4.0.1] - 2023-07-11 - Fixes duplicate users in users search queries when user is associated to multiple tenants - ## [4.0.0] - 2023-06-02 ### Changes - Support for multitenancy - - New tables `apps` and `tenants` have been added. - - Schema of tables have been changed, adding `app_id` and `tenant_id` columns in tables and constraints & indexes have been modified to include this columns. - - New user tables have been added to map users to apps and tenants. - - New tables for multitenancy have been added. + - New tables `apps` and `tenants` have been added. + - Schema of tables have been changed, adding `app_id` and `tenant_id` columns in tables and constraints & indexes + have been modified to include this columns. + - New user tables have been added to map users to apps and tenants. + - New tables for multitenancy have been added. - Increased transaction retry count to 50 from 20. ### Migration @@ -1085,19 +1087,19 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ ### Migration - If using `access_token_signing_key_dynamic` false in the core: - - ```sql - ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(true); - ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; + - ```sql + ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(true); + ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; ``` - - ```sql + - ```sql INSERT INTO jwt_signing_keys(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 session_access_token_signing_keys; ``` - If using `access_token_signing_key_dynamic` true (or not set) in the core: - - ```sql - ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(false); - ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; + - ```sql + ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(false); + ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT; ``` ## [2.4.0] - 2023-03-30 @@ -1105,14 +1107,17 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_ - Support for Dashboard Search ## [2.3.0] - 2023-03-27 + - Support for TOTP recipe - Support for active users ### Database changes + - Add new tables for TOTP recipe: - - `totp_users` that stores the users that have enabled TOTP - - `totp_user_devices` that stores devices (each device has its own secret) for each user - - `totp_used_codes` that stores used codes for each user. This is to implement rate limiting and prevent replay attacks. + - `totp_users` that stores the users that have enabled TOTP + - `totp_user_devices` that stores devices (each device has its own secret) for each user + - `totp_used_codes` that stores used codes for each user. This is to implement rate limiting and prevent replay + attacks. - Add `user_last_active` table to store the last active time of a user. ## [2.2.0] - 2023-02-21 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53732985..a222aea4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,51 +1,70 @@ - # Contributing -We're so excited you're interested in helping with SuperTokens! We are happy to help you get started, even if you don't have any previous open-source experience :blush: +We're so excited you're interested in helping with SuperTokens! We are happy to help you get started, even if you don't +have any previous open-source experience :blush: ## New to Open Source? -1. Take a look at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) -2. Go thorugh the [SuperTokens Code of Conduct](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CODE_OF_CONDUCT.md) + +1. Take a look + at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) +2. Go thorugh + the [SuperTokens Code of Conduct](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CODE_OF_CONDUCT.md) ## Where to ask Questions? -1. Check our [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) to see if someone has already answered your question. -2. Join our community on [Discord](https://supertokens.io/discord) and feel free to ask us your questions +1. Check our [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) to see if someone has + already answered your question. +2. Join our community on [Discord](https://supertokens.io/discord) and feel free to ask us your questions ## Development Setup + ### Prerequisites + - OS: Linux or macOS - IDE: Intellij (recommended) or equivalent IDE - PostgreSQL ### Project Setup -1. Setup the `supertokens-core` by following [this guide](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#development-setup). If you are not modifying the `supertokens-core` repo, then you do not need to fork that. + +1. Setup the `supertokens-core` by + following [this guide](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#development-setup). + If you are not modifying the `supertokens-core` repo, then you do not need to fork that. 2. Start PostgreSQL on port `5432`, listening to `locahost` or `0.0.0.0`. 3. Create a PostgreSQL user (if not already exists) with username `root` and password `root` 4. Create a database called `supertokens`. 5. Fork the `supertokens-pstgresql-plugin` repository -6. Open `modules.txt` in the `supertokens-root` directory and change it so that it looks like (the last line has changed): +6. Open `modules.txt` in the `supertokens-root` directory and change it so that it looks like (the last line has + changed): ``` // put module name like module name,branch name,github username(if contributing with a forked repository) and then call ./loadModules script core,master plugin-interface,master postgresql-plugin,master, ``` -7. Run `./loadModules` in the `supertokens-root` directory. This will clone your forked `supertokens-postgresql-plugin` repo. -8. Follow the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#modifying-code) guide from `supertokens-core` repo for modifying and testing. +7. Run `./loadModules` in the `supertokens-root` directory. This will clone your forked `supertokens-postgresql-plugin` + repo. +8. Follow + the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#modifying-code) + guide from `supertokens-core` repo for modifying and testing. ## Pull Request + 1. Before submitting a pull request make sure all tests have passed -2. Reference the relevant issue or pull request and give a clear description of changes/features added when submitting a pull request +2. Reference the relevant issue or pull request and give a clear description of changes/features added when submitting a + pull request 3. Make sure the PR title follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) specification ## SuperTokens Community -SuperTokens is made possible by a passionate team and a strong community of developers. If you have any questions or would like to get more involved in the SuperTokens community you can check out: - - [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) - - [Discord](https://supertokens.io/discord) - - [Twitter](https://twitter.com/supertokensio) - - or [email us](mailto:team@supertokens.io) - + +SuperTokens is made possible by a passionate team and a strong community of developers. If you have any questions or +would like to get more involved in the SuperTokens community you can check out: + +- [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) +- [Discord](https://supertokens.io/discord) +- [Twitter](https://twitter.com/supertokensio) +- or [email us](mailto:team@supertokens.io) + Additional resources you might find useful: - - [SuperTokens Docs](https://supertokens.io/docs/community/getting-started/installation) - - [Blog Posts](https://supertokens.io/blog/) + +- [SuperTokens Docs](https://supertokens.io/docs/community/getting-started/installation) +- [Blog Posts](https://supertokens.io/blog/) diff --git a/LICENSE.md b/LICENSE.md index e105852e..7a85c9c7 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,191 +1,192 @@ - Copyright (c) 2020, 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 software except in compliance with the License. A copy - of the License is available below the line. - - 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. +Copyright (c) 2020, 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 software except in compliance with the License. A copy +of the License is available below the line. + +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. ------------------------------------------------------------------------------- + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md index cc88cb0b..d44d464f 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,32 @@ - ![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png) # PostgreSQL plugin for SuperTokens Community + chat on Discord ## About + This plugin is responsible for interfacing between SuperTokens Community version and an instance of PostgreSQL. Learn more at https://supertokens.io ## Documentation + To see documentation, please click [here](https://supertokens.io/docs/community/introduction). ## Contributing -Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CONTRIBUTING.md) file in this repo. + +Please refer to +the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CONTRIBUTING.md) file in +this repo. ## Contact us -For any queries, or support requests, please email us at team@supertokens.io, or join our [Discord](supertokens.io/discord) server. + +For any queries, or support requests, please email us at team@supertokens.io, or join +our [Discord](supertokens.io/discord) server. # Authors + Created with :heart: by the folks at SuperTokens.io. diff --git a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java index 534e706a..acab61f5 100644 --- a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java +++ b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java @@ -149,7 +149,8 @@ static boolean isAlreadyInitialised(Start start) { return getInstance(start) != null && getInstance(start).hikariDataSource != null; } - static void initPool(Start start, boolean shouldWait, PostConnectCallback postConnectCallback) throws DbInitException { + static void initPool(Start start, boolean shouldWait, PostConnectCallback postConnectCallback) + throws DbInitException { if (isAlreadyInitialised(start)) { return; } diff --git a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java index db0c9785..179a871b 100644 --- a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java +++ b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java @@ -26,14 +26,14 @@ public interface QueryExecutorTemplate { static T execute(Start start, String QUERY, PreparedStatementValueSetter setter, - ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { + ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { try (Connection con = ConnectionPool.getConnection(start)) { return execute(con, QUERY, setter, mapper); } } static T execute(Connection con, String QUERY, PreparedStatementValueSetter setter, - ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { + ResultSetValueExtractor mapper) throws SQLException, StorageQueryException { if (setter == null) setter = PreparedStatementValueSetter.NO_OP_SETTER; try (PreparedStatement pst = con.prepareStatement(QUERY)) { diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 0ee1839f..a0eea865 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -843,7 +843,8 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi } } else if (className.equals(TOTPStorage.class.getName())) { try { - TOTPDevice device = new TOTPDevice(userId, "testDevice", "secret", 0, 30, false, System.currentTimeMillis()); + TOTPDevice device = new TOTPDevice(userId, "testDevice", "secret", 0, 30, false, + System.currentTimeMillis()); this.startTransaction(con -> { try { long now = System.currentTimeMillis(); @@ -886,7 +887,7 @@ public String[] getProtectedConfigsFromSuperTokensSaaSUsers() { @Override public AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, String email, String passwordHash, - long timeJoined) + long timeJoined) throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, TenantOrAppNotFoundException { try { @@ -1196,8 +1197,10 @@ public boolean isEmailVerified(AppIdentifier appIdentifier, String userId, Strin } @Override - public void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, String supertokensUserId, String externalUserId) throws StorageQueryException { - EmailVerificationQueries.updateIsEmailVerifiedToExternalUserId(this, appIdentifier, supertokensUserId, externalUserId); + public void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, String supertokensUserId, + String externalUserId) throws StorageQueryException { + EmailVerificationQueries.updateIsEmailVerifiedToExternalUserId(this, appIdentifier, supertokensUserId, + externalUserId); } @Override @@ -1718,10 +1721,10 @@ public void createCode(TenantIdentifier tenantIdentifier, PasswordlessCode code) @Override public AuthRecipeUserInfo createUser(TenantIdentifier tenantIdentifier, - String id, - @javax.annotation.Nullable String email, - @javax.annotation.Nullable - String phoneNumber, long timeJoined) + String id, + @javax.annotation.Nullable String email, + @javax.annotation.Nullable + String phoneNumber, long timeJoined) throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, DuplicateUserIdException, TenantOrAppNotFoundException { @@ -2242,7 +2245,8 @@ public boolean updateOrDeleteExternalUserIdInfo(AppIdentifier appIdentifier, Str } @Override - public HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier, ArrayList userIds) + public HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier, + ArrayList userIds) throws StorageQueryException { try { return UserIdMappingQueries.getUserIdMappingWithUserIds(this, appIdentifier, userIds); @@ -2361,7 +2365,8 @@ public TenantConfig[] getAllTenants() throws StorageQueryException { } @Override - public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId) + public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String userId) throws TenantOrAppNotFoundException, UnknownUserIdException, StorageQueryException, DuplicateEmailException, DuplicateThirdPartyUserException, DuplicatePhoneNumberException { Connection sqlCon = (Connection) con.getConnection(); @@ -2660,7 +2665,8 @@ public void createDevice(AppIdentifier appIdentifier, TOTPDevice device) } @Override - public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, TOTPDevice device) + public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + TOTPDevice device) throws StorageQueryException, DeviceAlreadyExistsException, TenantOrAppNotFoundException { Connection sqlCon = (Connection) con.getConnection(); try { @@ -2673,9 +2679,9 @@ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentif ServerErrorMessage errMsg = ((PSQLException) actualException).getServerErrorMessage(); if (isPrimaryKeyError(errMsg, Config.getConfig(this).getTotpUserDevicesTable())) { - throw new DeviceAlreadyExistsException(); + throw new DeviceAlreadyExistsException(); } else if (isForeignKeyConstraintError(errMsg, Config.getConfig(this).getTotpUsersTable(), "app_id")) { - throw new TenantOrAppNotFoundException(appIdentifier); + throw new TenantOrAppNotFoundException(appIdentifier); } } throw new StorageQueryException(e); @@ -2683,7 +2689,8 @@ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentif } @Override - public TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, String deviceName) throws StorageQueryException { + public TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, + String deviceName) throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); try { return TOTPQueries.getDeviceByName_Transaction(this, sqlCon, appIdentifier, userId, deviceName); @@ -2753,7 +2760,7 @@ public void updateDeviceName(AppIdentifier appIdentifier, String userId, String if (e instanceof PSQLException) { ServerErrorMessage errMsg = ((PSQLException) e).getServerErrorMessage(); if (isPrimaryKeyError(errMsg, Config.getConfig(this).getTotpUserDevicesTable())) { - throw new DeviceAlreadyExistsException(); + throw new DeviceAlreadyExistsException(); } } throw new StorageQueryException(e); @@ -2961,7 +2968,8 @@ public void linkAccounts_Transaction(AppIdentifier appIdentifier, TransactionCon } @Override - public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, String recipeUserId) + public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, + String recipeUserId) throws StorageQueryException { try { Connection sqlCon = (Connection) con.getConnection(); @@ -2994,7 +3002,8 @@ public boolean checkIfUsesAccountLinking(AppIdentifier appIdentifier) throws Sto } @Override - public int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException { + public int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) + throws StorageQueryException { try { return ActiveUsersQueries.countUsersActiveSinceAndHasMoreThanOneLoginMethod(this, appIdentifier, sinceTime); } catch (SQLException e) { @@ -3023,11 +3032,13 @@ public UserIdMapping getUserIdMapping_Transaction(TransactionConnection con, App try { Connection sqlCon = (Connection) con.getConnection(); if (isSuperTokensUserId) { - return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId_Transaction(this, sqlCon, appIdentifier, + return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId_Transaction(this, sqlCon, + appIdentifier, userId); } - return UserIdMappingQueries.getUserIdMappingWithExternalUserId_Transaction(this, sqlCon, appIdentifier, userId); + return UserIdMappingQueries.getUserIdMappingWithExternalUserId_Transaction(this, sqlCon, appIdentifier, + userId); } catch (SQLException e) { throw new StorageQueryException(e); } @@ -3048,7 +3059,8 @@ public UserIdMapping[] getUserIdMapping_Transaction(TransactionConnection con, A } @Override - public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier) throws StorageQueryException { + public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier) + throws StorageQueryException { try { return GeneralQueries.getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(this, appIdentifier); } catch (SQLException e) { @@ -3057,7 +3069,9 @@ public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier ap } @Override - public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException { + public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(AppIdentifier appIdentifier, + long sinceTime) + throws StorageQueryException { try { return ActiveUsersQueries.countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(this, appIdentifier, sinceTime); diff --git a/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java b/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java index a40b0a1e..9bc1ced3 100644 --- a/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java +++ b/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java @@ -14,7 +14,7 @@ * under the License. */ - package io.supertokens.storage.postgresql.annotations; +package io.supertokens.storage.postgresql.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -25,11 +25,15 @@ * Annotation to provide a description for a configuration fields. To be used on the fields of `CoreConfig` and config * class in the plugin like `PostgreSQLConfig`, `MysqlConfig`, etc. */ -@Retention(RetentionPolicy.RUNTIME) // Make annotation accessible at runtime so that config descriptions can be read from API +@Retention(RetentionPolicy.RUNTIME) +// Make annotation accessible at runtime so that config descriptions can be read from API @Target(ElementType.FIELD) // Annotation can only be applied to fields public @interface DashboardInfo { String description() default ""; + boolean isOptional() default false; + String defaultValue() default ""; + boolean isEditable() default false; } diff --git a/src/main/java/io/supertokens/storage/postgresql/config/Config.java b/src/main/java/io/supertokens/storage/postgresql/config/Config.java index 41088d73..1ce21e06 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/Config.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/Config.java @@ -53,7 +53,8 @@ private static Config getInstance(Start start) { return (Config) start.getResourceDistributor().getResource(RESOURCE_KEY); } - public static void loadConfig(Start start, JsonObject configJson, Set logLevels, TenantIdentifier tenantIdentifier) + public static void loadConfig(Start start, JsonObject configJson, Set logLevels, + TenantIdentifier tenantIdentifier) throws InvalidConfigException { if (getInstance(start) != null) { return; 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 7ff9daa1..a1ff555c 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -46,72 +46,102 @@ public class PostgreSQLConfig { @JsonProperty @ConnectionPoolProperty - @DashboardInfo(description = "Defines the connection pool size to PostgreSQL. Please see https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing", defaultValue = "10", isOptional = true, isEditable = true) + @DashboardInfo( + description = "Defines the connection pool size to PostgreSQL. Please see https://github" + + ".com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing", + defaultValue = "10", isOptional = true, isEditable = true) private int postgresql_connection_pool_size = 10; @JsonProperty @UserPoolProperty - @DashboardInfo(description = "Specify the postgresql host url here. For example: - \"localhost\" - \"192.168.0.1\" - \"\" - \"example.com\"", defaultValue = "\"localhost\"", isOptional = true) + @DashboardInfo( + description = "Specify the postgresql host url here. For example: - \"localhost\" - \"192.168.0.1\" - " + + "\"\" - \"example.com\"", + defaultValue = "\"localhost\"", isOptional = true) private String postgresql_host = null; @JsonProperty @UserPoolProperty - @DashboardInfo(description = "Specify the port to use when connecting to PostgreSQL instance.", defaultValue = "5432", isOptional = true) + @DashboardInfo(description = "Specify the port to use when connecting to PostgreSQL instance.", + defaultValue = "5432", isOptional = true) private int postgresql_port = -1; @JsonProperty @ConnectionPoolProperty - @DashboardInfo(description = "The PostgreSQL user to use to query the database. If the relevant tables are not already created by you, this user should have the ability to create new tables. To see the tables needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database-setup/postgresql", defaultValue = "null") + @DashboardInfo( + description = "The PostgreSQL user to use to query the database. If the relevant tables are not already " + + "created by you, this user should have the ability to create new tables. To see the tables " + + "needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database" + + "-setup/postgresql", + defaultValue = "null") private String postgresql_user = null; @JsonProperty @ConnectionPoolProperty - @DashboardInfo(description = "Password for the PostgreSQL user. If you have not set a password make this an empty string.", defaultValue = "null") + @DashboardInfo( + description = "Password for the PostgreSQL user. If you have not set a password make this an empty string.", + defaultValue = "null") private String postgresql_password = null; @JsonProperty @UserPoolProperty - @DashboardInfo(description = "The database name to store SuperTokens related data.", defaultValue = "\"supertokens\"", isOptional = true) + @DashboardInfo(description = "The database name to store SuperTokens related data.", + defaultValue = "\"supertokens\"", isOptional = true) private String postgresql_database_name = null; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "A prefix to add to all table names managed by SuperTokens. An \"_\" will be added between this prefix and the actual table name if the prefix is defined.", defaultValue = "\"\"", isOptional = true) + @DashboardInfo( + description = "A prefix to add to all table names managed by SuperTokens. An \"_\" will be added between " + + "this prefix and the actual table name if the prefix is defined.", + defaultValue = "\"\"", isOptional = true) private String postgresql_table_names_prefix = ""; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "Specify the name of the table that will store secret keys and app info necessary for the functioning sessions.", defaultValue = "\"key_value\"", isOptional = true) + @DashboardInfo( + description = "Specify the name of the table that will store secret keys and app info necessary for the " + + "functioning sessions.", + defaultValue = "\"key_value\"", isOptional = true) private String postgresql_key_value_table_name = null; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "Specify the name of the table that will store the session info for users.", defaultValue = "\"session_info\"", isOptional = true) + @DashboardInfo(description = "Specify the name of the table that will store the session info for users.", + defaultValue = "\"session_info\"", isOptional = true) private String postgresql_session_info_table_name = null; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "Specify the name of the table that will store the user information, along with their email and hashed password.", defaultValue = "\"emailpassword_users\"", isOptional = true) + @DashboardInfo( + description = "Specify the name of the table that will store the user information, along with their email" + + " and hashed password.", + defaultValue = "\"emailpassword_users\"", isOptional = true) private String postgresql_emailpassword_users_table_name = null; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "Specify the name of the table that will store the password reset tokens for users.", defaultValue = "\"emailpassword_pswd_reset_tokens\"", isOptional = true) + @DashboardInfo(description = "Specify the name of the table that will store the password reset tokens for users.", + defaultValue = "\"emailpassword_pswd_reset_tokens\"", isOptional = true) private String postgresql_emailpassword_pswd_reset_tokens_table_name = null; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "Specify the name of the table that will store the email verification tokens for users.", defaultValue = "\"emailverification_tokens\"", isOptional = true) + @DashboardInfo( + description = "Specify the name of the table that will store the email verification tokens for users.", + defaultValue = "\"emailverification_tokens\"", isOptional = true) private String postgresql_emailverification_tokens_table_name = null; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "Specify the name of the table that will store the verified email addresses.", defaultValue = "\"emailverification_verified_emails\"", isOptional = true) + @DashboardInfo(description = "Specify the name of the table that will store the verified email addresses.", + defaultValue = "\"emailverification_verified_emails\"", isOptional = true) private String postgresql_emailverification_verified_emails_table_name = null; @JsonProperty @NotConflictingWithinUserPool - @DashboardInfo(description = "Specify the name of the table that will store the thirdparty recipe users.", defaultValue = "\"thirdparty_users\"", isOptional = true) + @DashboardInfo(description = "Specify the name of the table that will store the thirdparty recipe users.", + defaultValue = "\"thirdparty_users\"", isOptional = true) private String postgresql_thirdparty_users_table_name = null; @JsonProperty @@ -121,25 +151,35 @@ public class PostgreSQLConfig { @JsonProperty @IgnoreForAnnotationCheck - @DashboardInfo(description = "Specify the PostgreSQL connection URI in the following format: postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2... Values provided via other configs will override values provided by this config.", defaultValue = "null", isOptional = true) + @DashboardInfo( + description = "Specify the PostgreSQL connection URI in the following format: " + + "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2... Values provided " + + "via other configs will override values provided by this config.", + defaultValue = "null", isOptional = true) private String postgresql_connection_uri = null; @ConnectionPoolProperty - @DashboardInfo(description = "The connection attributes of the PostgreSQL database.", defaultValue = "\"allowPublicKeyRetrieval=true\"", isOptional = true) + @DashboardInfo(description = "The connection attributes of the PostgreSQL database.", + defaultValue = "\"allowPublicKeyRetrieval=true\"", isOptional = true) private String postgresql_connection_attributes = "allowPublicKeyRetrieval=true"; @ConnectionPoolProperty - @DashboardInfo(description = "The scheme of the PostgreSQL database.", defaultValue = "\"postgresql\"", isOptional = true) + @DashboardInfo(description = "The scheme of the PostgreSQL database.", defaultValue = "\"postgresql\"", + isOptional = true) private String postgresql_connection_scheme = "postgresql"; @JsonProperty @ConnectionPoolProperty - @DashboardInfo(description = "Timeout in milliseconds for the idle connections to be closed.", defaultValue = "60000", isOptional = true, isEditable = true) + @DashboardInfo(description = "Timeout in milliseconds for the idle connections to be closed.", + defaultValue = "60000", isOptional = true, isEditable = true) private long postgresql_idle_connection_timeout = 60000; @JsonProperty @ConnectionPoolProperty - @DashboardInfo(description = "Minimum number of idle connections to be kept active. If not set, minimum idle connections will be same as the connection pool size.", defaultValue = "null", isOptional = true, isEditable = true) + @DashboardInfo( + description = "Minimum number of idle connections to be kept active. If not set, minimum idle connections" + + " will be same as the connection pool size.", + defaultValue = "null", isOptional = true, isEditable = true) private Integer postgresql_minimum_idle_connections = null; @IgnoreForAnnotationCheck @@ -439,7 +479,8 @@ private void validateAndNormalise(boolean skipValidation) throws InvalidConfigEx } catch (Exception e) { throw new InvalidConfigException( "The provided postgresql connection URI has an incorrect format. Please use a format like " - + "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2..."); + + + "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2..."); } } else { if (this.getUser() == null) { @@ -613,21 +654,26 @@ private void validateAndNormalise(boolean skipValidation) throws InvalidConfigEx } if (postgresql_emailpassword_pswd_reset_tokens_table_name != null) { - postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaToTableName(postgresql_emailpassword_pswd_reset_tokens_table_name); + postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaToTableName( + postgresql_emailpassword_pswd_reset_tokens_table_name); } else { - postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaAndPrefixToTableName("emailpassword_pswd_reset_tokens"); + postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaAndPrefixToTableName( + "emailpassword_pswd_reset_tokens"); } if (postgresql_emailverification_tokens_table_name != null) { - postgresql_emailverification_tokens_table_name = addSchemaToTableName(postgresql_emailverification_tokens_table_name); + postgresql_emailverification_tokens_table_name = addSchemaToTableName( + postgresql_emailverification_tokens_table_name); } else { postgresql_emailverification_tokens_table_name = addSchemaAndPrefixToTableName("emailverification_tokens"); } if (postgresql_emailverification_verified_emails_table_name != null) { - postgresql_emailverification_verified_emails_table_name = addSchemaToTableName(postgresql_emailverification_verified_emails_table_name); + postgresql_emailverification_verified_emails_table_name = addSchemaToTableName( + postgresql_emailverification_verified_emails_table_name); } else { - postgresql_emailverification_verified_emails_table_name = addSchemaAndPrefixToTableName("emailverification_verified_emails"); + postgresql_emailverification_verified_emails_table_name = addSchemaAndPrefixToTableName( + "emailverification_verified_emails"); } if (postgresql_thirdparty_users_table_name != null) { @@ -679,10 +725,11 @@ public String getConnectionPoolId() { try { String fieldName = field.getName(); String fieldValue = field.get(this) != null ? field.get(this).toString() : null; - if(fieldValue == null) { + if (fieldValue == null) { continue; } - // To ensure a unique connectionPoolId we include the database password and use the "|db_pass|" identifier. + // To ensure a unique connectionPoolId we include the database password and use the "|db_pass|" + // identifier. // This facilitates easy removal of the password from logs when necessary. if (fieldName.equals("postgresql_password")) { connectionPoolId.append("|db_pass|" + fieldValue + "|db_pass"); 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 3a39c384..327ed6ce 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java @@ -50,7 +50,8 @@ public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier }); } - public static int countUsersActiveSinceAndHasMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier, long sinceTime) + public static int countUsersActiveSinceAndHasMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier, + long sinceTime) throws SQLException, StorageQueryException { // TODO: Active users are present only on public tenant and MFA users may be present on different storages String QUERY = "SELECT count(1) as c FROM (" @@ -121,30 +122,32 @@ public static void deleteUserActive_Transaction(Connection con, Start start, App }); } - public static int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) + public static int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(Start start, + AppIdentifier appIdentifier, + long sinceTime) throws SQLException, StorageQueryException { // TODO: Active users are present only on public tenant and MFA users may be present on different storages String QUERY = - "SELECT COUNT (DISTINCT user_id) as c FROM (" - + " (" // users with more than one login method - + " SELECT primary_or_recipe_user_id AS user_id FROM (" - + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" - + " FROM " + getConfig(start).getAppIdToUserIdTable() - + " WHERE app_id = ? AND primary_or_recipe_user_id IN (" - + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() - + " WHERE app_id = ? AND last_active_time >= ?" - + " )" - + " GROUP BY (app_id, primary_or_recipe_user_id)" - + " ) AS nloginmethods" - + " WHERE num_login_methods > 1" - + " ) UNION (" // TOTP users - + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() - + " WHERE app_id = ? AND user_id IN (" - + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() - + " WHERE app_id = ? AND last_active_time >= ?" - + " )" - + " )" - + ") AS all_users"; + "SELECT COUNT (DISTINCT user_id) as c FROM (" + + " (" // users with more than one login method + + " SELECT primary_or_recipe_user_id AS user_id FROM (" + + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" + + " FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? AND primary_or_recipe_user_id IN (" + + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() + + " WHERE app_id = ? AND last_active_time >= ?" + + " )" + + " GROUP BY (app_id, primary_or_recipe_user_id)" + + " ) AS nloginmethods" + + " WHERE num_login_methods > 1" + + " ) UNION (" // TOTP users + + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() + + " WHERE app_id = ? AND user_id IN (" + + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable() + + " WHERE app_id = ? AND last_active_time >= ?" + + " )" + + " )" + + ") AS all_users"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); 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..89e85607 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java @@ -51,7 +51,7 @@ public static String getQueryToCreateDashboardUsersTable(Start start) { + " 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" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -119,7 +119,8 @@ public static boolean deleteDashboardUserWithUserId(Start start, AppIdentifier a } - public static DashboardUser[] getAllDashBoardUsers(Start start, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { + public static DashboardUser[] getAllDashBoardUsers(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getDashboardUsersTable() + " WHERE app_id = ? ORDER BY time_joined ASC"; return QueryExecutorTemplate.execute(start, QUERY, @@ -172,8 +173,9 @@ public static boolean updateDashboardUsersPasswordWithUserId_Transaction(Start s return rowsUpdated > 0; } - public static void createDashboardSession(Start start, AppIdentifier appIdentifier, String userId, String sessionId, long timeCreated, - long expiry) throws SQLException, StorageQueryException { + public static void createDashboardSession(Start start, AppIdentifier appIdentifier, String userId, String sessionId, + long timeCreated, + long expiry) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + Config.getConfig(start).getDashboardSessionsTable() + "(app_id, user_id, session_id, time_created, expiry)" + " VALUES(?, ?, ?, ?, ?)"; QueryExecutorTemplate.update(start, QUERY, pst -> { @@ -185,7 +187,8 @@ public static void createDashboardSession(Start start, AppIdentifier appIdentifi }); } - public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppIdentifier appIdentifier, String sessionId) + public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppIdentifier appIdentifier, + String sessionId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND session_id = ?"; @@ -200,7 +203,8 @@ public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppI }); } - public static DashboardSessionInfo[] getAllSessionsForUserId(Start start, AppIdentifier appIdentifier, String userId) + public static DashboardSessionInfo[] getAllSessionsForUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND user_id = ?"; @@ -234,7 +238,8 @@ public static DashboardUser getDashboardUserByEmail(Start start, AppIdentifier a }); } - public static boolean deleteDashboardUserSessionWithSessionId(Start start, AppIdentifier appIdentifier, String sessionId) + public static boolean deleteDashboardUserSessionWithSessionId(Start start, AppIdentifier appIdentifier, + String sessionId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND session_id = ?"; 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..efed6c7f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java @@ -285,7 +285,9 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -375,8 +377,10 @@ public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIde } } - private static UserInfoPartial getUserInfoUsingId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, - String id) throws SQLException, StorageQueryException { + private static UserInfoPartial getUserInfoUsingId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String id) + throws SQLException, StorageQueryException { // we don't need a FOR UPDATE here because this is already part of a transaction, and locked on // app_id_to_user_id table String QUERY = "SELECT user_id, email, password_hash, time_joined FROM " @@ -426,7 +430,7 @@ public static List getUsersInfoUsingIdList(Start start, Set } public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, - AppIdentifier appIdentifier) + AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { // No need to filter based on tenantId because the id list is already filtered for a tenant @@ -456,6 +460,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start, } return Collections.emptyList(); } + public static String lockEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String email) @@ -475,7 +480,7 @@ public static String lockEmail_Transaction(Start start, Connection con, } public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, - String email) + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getEmailPasswordUserToTenantTable() + " AS ep" + @@ -495,8 +500,9 @@ public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier te }); } - public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - String email) + public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getEmailPasswordUsersTable() + " AS ep" + @@ -526,11 +532,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC throw new UnknownUserIdException(); } - GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, + sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " + + "recipe_id, time_joined, primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; GeneralQueries.AccountLinkingInfo finalAccountLinkingInfo = accountLinkingInfo; @@ -545,14 +554,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC pst.setLong(8, userInfo.timeJoined); }); - GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), finalAccountLinkingInfo.primaryUserId); + GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), + finalAccountLinkingInfo.primaryUserId); } { // emailpassword_user_to_tenant String QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUserToTenantTable() + "(app_id, tenant_id, user_id, email)" + " VALUES(?, ?, ?, ?) " + " ON CONFLICT ON CONSTRAINT " - + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getEmailPasswordUserToTenantTable(), null, "pkey") + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), + getConfig(start).getEmailPasswordUserToTenantTable(), null, "pkey") + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { 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 6fd00660..9c70cf8f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java @@ -270,8 +270,10 @@ public static List isEmailVerified_transaction(Start start, Connection s // We have external user id stored in the email verification table, so we need to fetch the mapped userids for // calculating the verified emails - HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds_Transaction(start, - sqlCon, appIdentifier, supertokensUserIds); + HashMap supertokensUserIdToExternalUserIdMap = + UserIdMappingQueries.getUserIdMappingWithUserIds_Transaction( + start, + sqlCon, appIdentifier, supertokensUserIds); HashMap externalUserIdToSupertokensUserIdMap = new HashMap<>(); List supertokensOrExternalUserIdsToQuery = new ArrayList<>(); @@ -298,7 +300,8 @@ public static List isEmailVerified_transaction(Start start, Connection s } String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable() - + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + + + " WHERE app_id = ? AND user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + ") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")"; return execute(sqlCon, QUERY, pst -> { @@ -324,7 +327,7 @@ public static List isEmailVerified_transaction(Start start, Connection s } public static List isEmailVerified(Start start, AppIdentifier appIdentifier, - List userIdAndEmail) + List userIdAndEmail) throws SQLException, StorageQueryException { if (userIdAndEmail.isEmpty()) { @@ -339,7 +342,8 @@ public static List isEmailVerified(Start start, AppIdentifier appIdentif } // We have external user id stored in the email verification table, so we need to fetch the mapped userids for // calculating the verified emails - HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds(start, + HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds( + start, appIdentifier, supertokensUserIds); HashMap externalUserIdToSupertokensUserIdMap = new HashMap<>(); List supertokensOrExternalUserIdsToQuery = new ArrayList<>(); @@ -365,7 +369,8 @@ public static List isEmailVerified(Start start, AppIdentifier appIdentif supertokensOrExternalUserIdToEmailMap.put(supertokensOrExternalUserId, ue.email); } String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable() - + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + + + " WHERE app_id = ? AND user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) + ") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -476,7 +481,8 @@ public static boolean isUserIdBeingUsedForEmailVerification(Start start, AppIden } } - public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentifier appIdentifier, String supertokensUserId, String externalUserId) + public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentifier appIdentifier, + String supertokensUserId, String externalUserId) throws StorageQueryException { try { start.startTransaction((TransactionConnection con) -> { 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 94b54514..1608d3df 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -56,13 +56,15 @@ public class GeneralQueries { - private static boolean doesTableExists(Start start, Connection connection, String tableName) throws SQLException, StorageQueryException { + private static boolean doesTableExists(Start start, Connection connection, String tableName) + throws SQLException, StorageQueryException { try { String QUERY = "SELECT 1 FROM " + tableName + " LIMIT 1"; execute(connection, QUERY, NO_OP_SETTER, result -> null); return true; } catch (SQLException | StorageQueryException e) { - if (e.getMessage().contains("relation") && e.getMessage().contains(tableName) && e.getMessage().contains("does not exist")) { + if (e.getMessage().contains("relation") && e.getMessage().contains(tableName) && + e.getMessage().contains("does not exist")) { return false; } throw e; @@ -89,7 +91,8 @@ static String getQueryToCreateUsersTable(Start start) { + " 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," + + " 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() + @@ -120,12 +123,16 @@ static String getQueryToCreateUserPaginationIndex2(Start start) { 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);"; + + + "(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);"; + + + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id " + + "DESC);"; } static String getQueryToCreatePrimaryUserId(Start start) { @@ -221,7 +228,8 @@ private static String getQueryToCreateAppIdToUserIdTable(Start start) { + " 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," + + " 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" @@ -614,7 +622,7 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer + getConfig(start).getUserRolesTable() + "," + getConfig(start).getDashboardUsersTable() + "," + getConfig(start).getDashboardSessionsTable() + "," - + getConfig(start).getTotpUsedCodesTable() + "," + + getConfig(start).getTotpUsedCodesTable() + "," + getConfig(start).getTotpUserDevicesTable() + "," + getConfig(start).getTotpUsersTable(); update(start, DROP_QUERY, NO_OP_SETTER); @@ -774,7 +782,8 @@ public static boolean doesUserIdExist(Start start, AppIdentifier appIdentifier, }, ResultSet::next); } - public static boolean doesUserIdExist_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static boolean doesUserIdExist_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { // We query both tables cause there is a case where a primary user ID exists, but its associated // recipe user ID has been deleted AND there are other recipe user IDs linked to this primary user ID already. @@ -986,8 +995,11 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant usersFromQuery = new ArrayList<>(); } else { - String finalQuery = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM ( " + USER_SEARCH_TAG_CONDITION.toString() + " )" - + " AS finalResultTable ORDER BY primary_or_recipe_user_time_joined " + timeJoinedOrder + ", primary_or_recipe_user_id DESC "; + String finalQuery = + "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM ( " + + USER_SEARCH_TAG_CONDITION.toString() + " )" + + " AS finalResultTable ORDER BY primary_or_recipe_user_time_joined " + + timeJoinedOrder + ", primary_or_recipe_user_id DESC "; usersFromQuery = execute(start, finalQuery, pst -> { for (int i = 1; i <= queryList.size(); i++) { pst.setString(i, queryList.get(i - 1)); @@ -1023,9 +1035,12 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant recipeIdCondition = recipeIdCondition + " AND"; } String timeJoinedOrderSymbol = timeJoinedOrder.equals("ASC") ? ">" : "<"; - String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + getConfig(start).getUsersTable() + " WHERE " + String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + + getConfig(start).getUsersTable() + " WHERE " + recipeIdCondition + " (primary_or_recipe_user_time_joined " + timeJoinedOrderSymbol - + " ? OR (primary_or_recipe_user_time_joined = ? AND primary_or_recipe_user_id <= ?)) AND app_id = ? AND tenant_id = ?" + + + " ? OR (primary_or_recipe_user_time_joined = ? AND primary_or_recipe_user_id <= ?)) AND " + + "app_id = ? AND tenant_id = ?" + " ORDER BY primary_or_recipe_user_time_joined " + timeJoinedOrder + ", primary_or_recipe_user_id DESC LIMIT ?"; usersFromQuery = execute(start, QUERY, pst -> { @@ -1051,7 +1066,8 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant }); } else { String recipeIdCondition = RECIPE_ID_CONDITION.toString(); - String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + getConfig(start).getUsersTable() + " WHERE "; + String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + + getConfig(start).getUsersTable() + " WHERE "; if (!recipeIdCondition.equals("")) { QUERY += recipeIdCondition + " AND"; } @@ -1237,7 +1253,8 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction( // now that we have locks on all the relevant tables, we can read from them safely List userIds = ThirdPartyQueries.listUserIdsByThirdPartyInfo_Transaction(start, sqlCon, appIdentifier, thirdPartyId, thirdPartyUserId); - List result = getPrimaryUserInfoForUserIds_Transaction(start, sqlCon, appIdentifier, userIds); + List result = getPrimaryUserInfoForUserIds_Transaction(start, sqlCon, appIdentifier, + userIds); // this is going to order them based on oldest that joined to newest that joined. result.sort(Comparator.comparingLong(o -> o.timeJoined)); @@ -1335,9 +1352,9 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber(Start start, } public static AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(Start start, - TenantIdentifier tenantIdentifier, - String thirdPartyId, - String thirdPartyUserId) + TenantIdentifier tenantIdentifier, + String thirdPartyId, + String thirdPartyUserId) throws StorageQueryException, SQLException { String userId = ThirdPartyQueries.getUserIdByThirdPartyInfo(start, tenantIdentifier, thirdPartyId, thirdPartyUserId); @@ -1371,7 +1388,7 @@ public static AuthRecipeUserInfo getPrimaryUserInfoForUserId(Start start, AppIde } public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String id) + AppIdentifier appIdentifier, String id) throws SQLException, StorageQueryException { List ids = new ArrayList<>(); ids.add(id); @@ -1394,14 +1411,18 @@ private static List getPrimaryUserInfoForUserIds(Start start // which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column, // or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id // column - String QUERY = "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, aaru.tenant_id, aaru.time_joined FROM " + getConfig(start).getAppIdToUserIdTable() + " as au " + - "LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + - " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + - getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" - + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ") OR primary_or_recipe_user_id IN (" + - Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ")) AND app_id = ?) AND au.app_id = ?"; + String QUERY = + "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + + "aaru.tenant_id, aaru.time_joined FROM " + + getConfig(start).getAppIdToUserIdTable() + " as au " + + "LEFT JOIN " + getConfig(start).getUsersTable() + + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + + " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + + getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") OR primary_or_recipe_user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ")) AND app_id = ?) AND au.app_id = ?"; List allAuthUsersResult = execute(start, QUERY, pst -> { // IN user_id @@ -1475,8 +1496,8 @@ private static List getPrimaryUserInfoForUserIds(Start start } private static List getPrimaryUserInfoForUserIds_Transaction(Start start, Connection sqlCon, - AppIdentifier appIdentifier, - List userIds) + AppIdentifier appIdentifier, + List userIds) throws StorageQueryException, SQLException { if (userIds.size() == 0) { return new ArrayList<>(); @@ -1486,14 +1507,18 @@ private static List getPrimaryUserInfoForUserIds_Transaction // which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column, // or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id // column - String QUERY = "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, aaru.tenant_id, aaru.time_joined FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" + - " LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + - " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + - getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" - + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ") OR primary_or_recipe_user_id IN (" + - Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ")) AND app_id = ?) AND au.app_id = ?"; + String QUERY = + "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + + "aaru.tenant_id, aaru.time_joined FROM " + + getConfig(start).getAppIdToUserIdTable() + " as au" + + " LEFT JOIN " + getConfig(start).getUsersTable() + + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + + " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + + getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") OR primary_or_recipe_user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ")) AND app_id = ?) AND au.app_id = ?"; List allAuthUsersResult = execute(sqlCon, QUERY, pst -> { // IN user_id @@ -1530,10 +1555,13 @@ private static List getPrimaryUserInfoForUserIds_Transaction List loginMethods = new ArrayList<>(); loginMethods.addAll( - EmailPasswordQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); - loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); + EmailPasswordQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, + appIdentifier)); + loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, + appIdentifier)); loginMethods.addAll( - PasswordlessQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); + PasswordlessQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, + appIdentifier)); Map recipeUserIdToLoginMethodMap = new HashMap<>(); for (LoginMethod loginMethod : loginMethods) { @@ -1732,20 +1760,20 @@ public static int getUsersCountWithMoreThanOneLoginMethod(Start start, AppIdenti public static int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(Start start, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { String QUERY = - "SELECT COUNT (DISTINCT user_id) as c FROM (" - + " (" // Users with number of login methods > 1 - + " SELECT primary_or_recipe_user_id AS user_id FROM (" - + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" - + " FROM " + getConfig(start).getAppIdToUserIdTable() - + " WHERE app_id = ? " - + " GROUP BY (app_id, primary_or_recipe_user_id)" - + " ) AS nloginmethods" - + " WHERE num_login_methods > 1" - + " ) UNION (" // TOTP users - + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() - + " WHERE app_id = ?" - + " )" - + ") AS all_users"; + "SELECT COUNT (DISTINCT user_id) as c FROM (" + + " (" // Users with number of login methods > 1 + + " SELECT primary_or_recipe_user_id AS user_id FROM (" + + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" + + " FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? " + + " GROUP BY (app_id, primary_or_recipe_user_id)" + + " ) AS nloginmethods" + + " WHERE num_login_methods > 1" + + " ) UNION (" // TOTP users + + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable() + + " WHERE app_id = ?" + + " )" + + ") AS all_users"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -1768,7 +1796,8 @@ public static boolean checkIfUsesAccountLinking(Start start, AppIdentifier appId }); } - public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { GeneralQueries.AccountLinkingInfo accountLinkingInfo = new GeneralQueries.AccountLinkingInfo(userId, false); { @@ -1790,7 +1819,8 @@ public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, return accountLinkingInfo; } - public static void updateTimeJoinedForPrimaryUser_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String primaryUserId) + public static void updateTimeJoinedForPrimaryUser_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, String primaryUserId) throws SQLException, StorageQueryException { String QUERY = "UPDATE " + getConfig(start).getUsersTable() + " SET primary_or_recipe_user_time_joined = (SELECT MIN(time_joined) FROM " + 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..9e80fe48 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java @@ -52,14 +52,14 @@ static String getQueryToCreateJWTSigningTable(Start start) { 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," + + "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" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -75,7 +75,7 @@ public static List getJWTSigningKeys_Transaction(Start start, String QUERY = "SELECT * FROM " + getConfig(start).getJWTSigningKeysTable() + " WHERE app_id = ? ORDER BY created_at DESC FOR UPDATE"; - return execute(con, QUERY, pst -> pst.setString(1, appIdentifier.getAppId()),result -> { + return execute(con, QUERY, pst -> pst.setString(1, appIdentifier.getAppId()), result -> { List keys = new ArrayList<>(); while (result.next()) { 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 447ffb40..a6c9b749 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java @@ -53,7 +53,8 @@ static String getQueryToCreateTenantConfigsTable(Start start) { + "third_party_enabled BOOLEAN," + "is_first_factors_null BOOLEAN," + "is_third_party_providers_null BOOLEAN," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)" + + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") + + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)" + ");"; // @formatter:on } @@ -84,10 +85,12 @@ static String getQueryToCreateTenantThirdPartyProvidersTable(Start start) { + "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, 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" + + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -111,16 +114,20 @@ static String getQueryToCreateTenantThirdPartyProviderClientsTable(Start start) + "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") + + "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" + + " 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);"; + + getConfig(start).getTenantThirdPartyProviderClientsTable() + + " (connection_uri_domain, app_id, tenant_id, third_party_id);"; } public static String getQueryToCreateFirstFactorsTable(Start start) { @@ -136,7 +143,8 @@ public static String getQueryToCreateFirstFactorsTable(Start start) { + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, factor_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "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" + + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -159,14 +167,16 @@ public static String getQueryToCreateRequiredSecondaryFactorsTable(Start start) + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, factor_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "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" + + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE" + ");"; // @formatter:on } public static String getQueryToCreateTenantIdIndexForRequiredSecondaryFactorsTable(Start start) { return "CREATE INDEX IF NOT EXISTS tenant_default_required_factor_ids_tenant_id_index ON " - + getConfig(start).getTenantRequiredSecondaryFactorsTable() + " (connection_uri_domain, app_id, tenant_id);"; + + getConfig(start).getTenantRequiredSecondaryFactorsTable() + + " (connection_uri_domain, app_id, tenant_id);"; } @@ -186,10 +196,12 @@ private static void executeCreateTenantQueries(Start start, Connection sqlCon, T } MfaSqlHelper.createFirstFactors(start, sqlCon, tenantConfig.tenantIdentifier, tenantConfig.firstFactors); - MfaSqlHelper.createRequiredSecondaryFactors(start, sqlCon, tenantConfig.tenantIdentifier, tenantConfig.requiredSecondaryFactors); + MfaSqlHelper.createRequiredSecondaryFactors(start, sqlCon, tenantConfig.tenantIdentifier, + tenantConfig.requiredSecondaryFactors); } - public static void createTenantConfig(Start start, TenantConfig tenantConfig) throws StorageQueryException, StorageTransactionLogicException { + public static void createTenantConfig(Start start, TenantConfig tenantConfig) + throws StorageQueryException, StorageTransactionLogicException { start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); { @@ -205,7 +217,8 @@ public static void createTenantConfig(Start start, TenantConfig tenantConfig) th }); } - public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIdentifier) throws StorageQueryException { + public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIdentifier) + throws StorageQueryException { try { String QUERY = "DELETE FROM " + getConfig(start).getTenantConfigsTable() + " WHERE connection_uri_domain = ? AND app_id = ? AND tenant_id = ?"; @@ -223,7 +236,8 @@ public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIde } } - public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig) throws StorageQueryException, StorageTransactionLogicException { + public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig) + throws StorageQueryException, StorageTransactionLogicException { start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); { @@ -237,7 +251,8 @@ public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig) pst.setString(3, tenantConfig.tenantIdentifier.getTenantId()); }); if (rowsAffected == 0) { - throw new StorageTransactionLogicException(new TenantOrAppNotFoundException(tenantConfig.tenantIdentifier)); + throw new StorageTransactionLogicException( + new TenantOrAppNotFoundException(tenantConfig.tenantIdentifier)); } } @@ -260,16 +275,21 @@ public static TenantConfig[] getAllTenants(Start start) throws StorageQueryExcep try { // Map TenantIdentifier -> thirdPartyId -> clientType - HashMap>> providerClientsMap = ThirdPartyProviderClientSQLHelper.selectAll(start); + HashMap>> providerClientsMap = ThirdPartyProviderClientSQLHelper.selectAll( + start); // Map (tenantIdentifier) -> thirdPartyId -> provider - HashMap> providerMap = ThirdPartyProviderSQLHelper.selectAll(start, providerClientsMap); + HashMap> providerMap = + ThirdPartyProviderSQLHelper.selectAll( + start, providerClientsMap); // Map (tenantIdentifier) -> firstFactors HashMap firstFactorsMap = MfaSqlHelper.selectAllFirstFactors(start); // Map (tenantIdentifier) -> requiredSecondaryFactors - HashMap requiredSecondaryFactorsMap = MfaSqlHelper.selectAllRequiredSecondaryFactors(start); + HashMap requiredSecondaryFactorsMap = + MfaSqlHelper.selectAllRequiredSecondaryFactors( + start); return TenantConfigSQLHelper.selectAll(start, providerMap, firstFactorsMap, requiredSecondaryFactorsMap); } catch (SQLException throwables) { @@ -294,7 +314,7 @@ public static void addTenantIdInTargetStorage(Start start, TenantIdentifier tena } public static void addTenantIdInTargetStorage_Transaction(Start start, Connection con, - TenantIdentifier tenantIdentifier) throws + TenantIdentifier tenantIdentifier) throws SQLException, StorageQueryException { { if (Start.isTesting && simulateErrorInAddingTenantIdInTargetStorage_forTesting) { @@ -318,7 +338,7 @@ public static void addTenantIdInTargetStorage_Transaction(Start start, Connectio + "(app_id, created_at_time)" + " VALUES(?, ?) ON CONFLICT DO NOTHING"; update(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); - pst.setLong(2, currentTime); + pst.setLong(2, currentTime); }); } @@ -329,14 +349,14 @@ public static void addTenantIdInTargetStorage_Transaction(Start start, Connectio update(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); - pst.setLong(3, currentTime); + pst.setLong(3, currentTime); }); } } } public static void deleteTenantIdInTargetStorage(Start start, TenantIdentifier tenantIdentifier) - throws StorageQueryException { + throws StorageQueryException { try { if (tenantIdentifier.getTenantId().equals(TenantIdentifier.DEFAULT_TENANT_ID)) { // Delete the app 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..bfe1a2a9 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java @@ -388,7 +388,8 @@ public static void deleteCode_Transaction(Start start, Connection con, TenantIde }); } - public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenantIdentifier, String id, @Nullable String email, + public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenantIdentifier, String id, + @Nullable String email, @Nullable String phoneNumber, long timeJoined) throws StorageTransactionLogicException, StorageQueryException { return start.startTransaction(con -> { @@ -407,7 +408,9 @@ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenant { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -457,7 +460,7 @@ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenant } private static UserInfoWithTenantId[] getUserInfosWithTenant_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String userId) + AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { String QUERY = "SELECT pl_users.user_id as user_id, pl_users.email as email, " + "pl_users.phone_number as phone_number, pl_users_to_tenant.tenant_id as tenant_id " @@ -742,7 +745,7 @@ public static List getUsersInfoUsingIdList(Start start, Set } public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, - AppIdentifier appIdentifier) + AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { // No need to filter based on tenantId because the id list is already filtered for a tenant @@ -772,7 +775,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start, } private static UserInfoPartial getUserById_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, - String userId) + String userId) throws StorageQueryException, SQLException { // we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id // table @@ -810,8 +813,8 @@ public static List lockEmail_Transaction(Start start, Connection con, Ap } public static List lockPhoneAndTenant_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, - String phoneNumber) + AppIdentifier appIdentifier, + String phoneNumber) throws SQLException, StorageQueryException { String QUERY = "SELECT user_id FROM " + getConfig(start).getPasswordlessUsersTable() + @@ -829,7 +832,7 @@ public static List lockPhoneAndTenant_Transaction(Start start, Connectio } public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, - String email) + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getPasswordlessUserToTenantTable() + " AS pless" + @@ -849,8 +852,9 @@ public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier te }); } - public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - String email) + public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String email) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" + @@ -891,8 +895,9 @@ public static String getPrimaryUserByPhoneNumber(Start start, TenantIdentifier t }); } - public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - @Nonnull String phoneNumber) + public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + @Nonnull String phoneNumber) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" + @@ -922,11 +927,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC throw new UnknownUserIdException(); } - GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, + sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " + + "recipe_id, time_joined, primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -939,14 +947,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC pst.setLong(8, userInfo.timeJoined); }); - GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), accountLinkingInfo.primaryUserId); + GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), + accountLinkingInfo.primaryUserId); } { // passwordless_user_to_tenant String QUERY = "INSERT INTO " + getConfig(start).getPasswordlessUserToTenantTable() + "(app_id, tenant_id, user_id, email, phone_number)" + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT " - + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getPasswordlessUserToTenantTable(), null, "pkey") + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), + getConfig(start).getPasswordlessUserToTenantTable(), null, "pkey") + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { 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 8a8269d5..60270a65 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java @@ -32,7 +32,7 @@ public static String getQueryToCreateUsersTable(Start start) { + " 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" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -119,10 +119,13 @@ private static int insertUser_Transaction(Start start, Connection con, AppIdenti }); } - private static int insertDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, TOTPDevice device) + private static int insertDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + TOTPDevice device) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUserDevicesTable() - + " (app_id, user_id, device_name, secret_key, period, skew, verified, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + + + " (app_id, user_id, device_name, secret_key, period, skew, verified, created_at) VALUES (?, ?, ?, ?, " + + "?, ?, ?, ?)"; return update(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -136,13 +139,15 @@ private static int insertDevice_Transaction(Start start, Connection con, AppIden }); } - public static void createDevice_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, TOTPDevice device) + public static void createDevice_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + TOTPDevice device) throws SQLException, StorageQueryException { insertUser_Transaction(start, sqlCon, appIdentifier, device.userId); insertDevice_Transaction(start, sqlCon, appIdentifier, device); } - public static TOTPDevice getDeviceByName_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId, String deviceName) + public static TOTPDevice getDeviceByName_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + String userId, String deviceName) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getTotpUserDevicesTable() + " WHERE app_id = ? AND user_id = ? AND device_name = ? FOR UPDATE;"; @@ -170,7 +175,8 @@ public static int markDeviceAsVerified(Start start, AppIdentifier appIdentifier, }); } - public static int deleteDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String deviceName) + public static int deleteDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, + String deviceName) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + Config.getConfig(start).getTotpUserDevicesTable() + " WHERE app_id = ? AND user_id = ? AND device_name = ?;"; @@ -207,7 +213,8 @@ public static boolean removeUser(Start start, TenantIdentifier tenantIdentifier, return removedUsersCount > 0; } - public static int updateDeviceName(Start start, AppIdentifier appIdentifier, String userId, String oldDeviceName, String newDeviceName) + public static int updateDeviceName(Start start, AppIdentifier appIdentifier, String userId, String oldDeviceName, + String newDeviceName) throws StorageQueryException, SQLException { String QUERY = "UPDATE " + Config.getConfig(start).getTotpUserDevicesTable() + " SET device_name = ? WHERE app_id = ? AND user_id = ? AND device_name = ?;"; @@ -238,7 +245,8 @@ public static TOTPDevice[] getDevices(Start start, AppIdentifier appIdentifier, }); } - public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) + public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String userId) throws StorageQueryException, SQLException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getTotpUserDevicesTable() + " WHERE app_id = ? AND user_id = ? FOR UPDATE;"; @@ -257,10 +265,13 @@ public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, A } - public static int insertUsedCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, TOTPUsedCode code) + public static int insertUsedCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, + TOTPUsedCode code) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUsedCodesTable() - + " (app_id, tenant_id, user_id, code, is_valid, expiry_time_ms, created_time_ms) VALUES (?, ?, ?, ?, ?, ?, ?);"; + + + " (app_id, tenant_id, user_id, code, is_valid, expiry_time_ms, created_time_ms) VALUES (?, ?, ?, ?, " + + "?, ?, ?);"; return update(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -278,7 +289,7 @@ public static int insertUsedCode_Transaction(Start start, Connection con, Tenant * order of creation time. */ public static TOTPUsedCode[] getAllUsedCodesDescOrder_Transaction(Start start, Connection con, - TenantIdentifier tenantIdentifier, String userId) + TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { // Take a lock based on the user id: String QUERY = "SELECT * FROM " + 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..964b53cd 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java @@ -117,7 +117,9 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -274,7 +276,7 @@ public static List getUsersInfoUsingIdList(Start start, Set } public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, - AppIdentifier appIdentifier) + AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined " @@ -305,7 +307,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start, public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifier appIdentifier, - String thirdPartyId, String thirdPartyUserId) + String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " @@ -327,7 +329,8 @@ public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifie }); } - public static List listUserIdsByThirdPartyInfo_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + public static List listUserIdsByThirdPartyInfo_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { @@ -388,7 +391,7 @@ public static void updateUserEmail_Transaction(Start start, Connection con, AppI } private static UserInfoPartial getUserInfoUsingUserId_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String userId) + AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { // we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id @@ -462,11 +465,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC throw new UnknownUserIdException(); } - GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, + sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)" + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " + + "recipe_id, time_joined, primary_or_recipe_user_time_joined)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -479,14 +485,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC pst.setLong(8, userInfo.timeJoined); }); - GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), accountLinkingInfo.primaryUserId); + GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), + accountLinkingInfo.primaryUserId); } { // thirdparty_user_to_tenant String QUERY = "INSERT INTO " + getConfig(start).getThirdPartyUserToTenantTable() + "(app_id, tenant_id, user_id, third_party_id, third_party_user_id)" + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT " - + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getThirdPartyUserToTenantTable(), null, "pkey") + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), + getConfig(start).getThirdPartyUserToTenantTable(), null, "pkey") + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); 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 a2388765..072571c1 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java @@ -55,7 +55,8 @@ public static String getQueryToCreateUserIdMappingTable(Start start) { + " 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" + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id, user_id)" + + " ON DELETE CASCADE" + ");"; // @formatter:on } @@ -65,7 +66,8 @@ public static String getQueryToCreateSupertokensUserIdIndexForUserIdMappingTable + getConfig(start).getUserIdMappingTable() + "(app_id, supertokens_user_id);"; } - public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, String superTokensUserId, String externalUserId, + 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() + " (app_id, supertokens_user_id, external_user_id, external_user_id_info)" + " VALUES(?, ?, ?, ?)"; @@ -78,7 +80,8 @@ public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, }); } - public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND supertokens_user_id = ?"; @@ -93,7 +96,8 @@ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, }); } - public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND external_user_id = ?"; @@ -110,7 +114,9 @@ public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppI } public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId(Start start, - AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { + AppIdentifier appIdentifier, + String userId) + throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND (supertokens_user_id = ? OR external_user_id = ?)"; @@ -201,7 +207,8 @@ public static HashMap getUserIdMappingWithUserIds_Transaction(St }); } - public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId) + public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND supertokens_user_id = ?"; @@ -217,7 +224,8 @@ public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppI public static boolean deleteUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { - String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND external_user_id = ?"; + String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() + + " WHERE app_id = ? AND external_user_id = ?"; // store the number of rows updated int rowUpdatedCount = update(start, QUERY, pst -> { @@ -229,8 +237,10 @@ public static boolean deleteUserIdMappingWithExternalUserId(Start start, AppIden } public static boolean updateOrDeleteExternalUserIdInfoWithSuperTokensUserId(Start start, - AppIdentifier appIdentifier, String userId, - @Nullable String externalUserIdInfo) throws SQLException, StorageQueryException { + AppIdentifier appIdentifier, + String userId, + @Nullable String externalUserIdInfo) + throws SQLException, StorageQueryException { String QUERY = "UPDATE " + Config.getConfig(start).getUserIdMappingTable() + " SET external_user_id_info = ? WHERE app_id = ? AND supertokens_user_id = ?"; @@ -245,7 +255,8 @@ public static boolean updateOrDeleteExternalUserIdInfoWithSuperTokensUserId(Star public static boolean updateOrDeleteExternalUserIdInfoWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId, - @Nullable String externalUserIdInfo) throws SQLException, StorageQueryException { + @Nullable String externalUserIdInfo) + throws SQLException, StorageQueryException { String QUERY = "UPDATE " + Config.getConfig(start).getUserIdMappingTable() + " SET external_user_id_info = ? WHERE app_id = ? AND external_user_id = ?"; @@ -258,7 +269,9 @@ public static boolean updateOrDeleteExternalUserIdInfoWithExternalUserId(Start s return rowUpdated > 0; } - public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND supertokens_user_id = ?"; @@ -273,7 +286,9 @@ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(S }); } - public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND external_user_id = ?"; @@ -289,8 +304,10 @@ public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start }); } - public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(Start start, Connection sqlCon, - AppIdentifier appIdentifier, String userId) + public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(Start start, + Connection sqlCon, + AppIdentifier appIdentifier, + String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND (supertokens_user_id = ? OR external_user_id = ?)"; 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 10fcb1a7..5825164c 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java @@ -142,7 +142,7 @@ public static void addPermissionToRoleOrDoNothingIfExists_Transaction(Start star } public static boolean deleteRole(Start start, AppIdentifier appIdentifier, - String role) + String role) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getRolesTable() + " WHERE app_id = ? AND role = ? ;"; diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java index b5abf91d..ccbab4f6 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java @@ -33,11 +33,13 @@ public static HashMap selectAllFirstFactors(Start st throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, factor_id FROM " + getConfig(start).getTenantFirstFactorsTable() + ";"; - return execute(start, QUERY, pst -> {}, result -> { + return execute(start, QUERY, pst -> { + }, result -> { HashMap> firstFactors = new HashMap<>(); while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); if (!firstFactors.containsKey(tenantIdentifier)) { firstFactors.put(tenantIdentifier, new ArrayList<>()); } @@ -58,7 +60,8 @@ public static HashMap selectAllRequiredSecondaryFact throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, factor_id FROM " + getConfig(start).getTenantRequiredSecondaryFactorsTable() + ";"; - return execute(start, QUERY, pst -> {}, result -> { + return execute(start, QUERY, pst -> { + }, result -> { HashMap> defaultRequiredFactors = new HashMap<>(); while (result.next()) { @@ -80,16 +83,18 @@ public static HashMap selectAllRequiredSecondaryFact }); } - public static void createFirstFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String[] firstFactors) + public static void createFirstFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, + String[] firstFactors) throws SQLException, StorageQueryException { if (firstFactors == null || firstFactors.length == 0) { return; } - String QUERY = "INSERT INTO " + getConfig(start).getTenantFirstFactorsTable() + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; + String QUERY = "INSERT INTO " + getConfig(start).getTenantFirstFactorsTable() + + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; for (String factorId : new HashSet<>(Arrays.asList(firstFactors))) { update(sqlCon, QUERY, pst -> { - pst.setString(1, tenantIdentifier.getConnectionUriDomain()); + pst.setString(1, tenantIdentifier.getConnectionUriDomain()); pst.setString(2, tenantIdentifier.getAppId()); pst.setString(3, tenantIdentifier.getTenantId()); pst.setString(4, factorId); @@ -97,13 +102,15 @@ public static void createFirstFactors(Start start, Connection sqlCon, TenantIden } } - public static void createRequiredSecondaryFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String[] requiredSecondaryFactors) + public static void createRequiredSecondaryFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, + String[] requiredSecondaryFactors) throws SQLException, StorageQueryException { if (requiredSecondaryFactors == null || requiredSecondaryFactors.length == 0) { return; } - String QUERY = "INSERT INTO " + getConfig(start).getTenantRequiredSecondaryFactorsTable() + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; + String QUERY = "INSERT INTO " + getConfig(start).getTenantRequiredSecondaryFactorsTable() + + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);"; for (String factorId : requiredSecondaryFactors) { update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getConnectionUriDomain()); diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java index 3c9c100a..3a57f9aa 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java @@ -39,13 +39,16 @@ public static class TenantConfigRowMapper implements RowMapper> providerMap, HashMap firstFactorsMap, HashMap requiredSecondaryFactorsMap) + public static TenantConfig[] selectAll(Start start, + HashMap> providerMap, + HashMap firstFactorsMap, + HashMap requiredSecondaryFactorsMap) throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, core_config," + " email_password_enabled, passwordless_enabled, third_party_enabled, " + " is_first_factors_null, is_third_party_providers_null FROM " + getConfig(start).getTenantConfigsTable() + ";"; - TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> {}, result -> { + TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> { + }, result -> { List temp = new ArrayList<>(); while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); ThirdPartyConfig.Provider[] providers; if (providerMap.containsKey(tenantIdentifier)) { providers = providerMap.get(tenantIdentifier).values().toArray(new ThirdPartyConfig.Provider[0]); } else { providers = new ThirdPartyConfig.Provider[0]; } - String[] firstFactors = firstFactorsMap.containsKey(tenantIdentifier) ? firstFactorsMap.get(tenantIdentifier) : new String[0]; + String[] firstFactors = + firstFactorsMap.containsKey(tenantIdentifier) ? firstFactorsMap.get(tenantIdentifier) : + new String[0]; - String[] requiredSecondaryFactors = requiredSecondaryFactorsMap.containsKey(tenantIdentifier) ? requiredSecondaryFactorsMap.get(tenantIdentifier) : new String[0]; + String[] requiredSecondaryFactors = requiredSecondaryFactorsMap.containsKey(tenantIdentifier) ? + requiredSecondaryFactorsMap.get(tenantIdentifier) : new String[0]; - temp.add(TenantConfigSQLHelper.TenantConfigRowMapper.getInstance(providers, firstFactors, requiredSecondaryFactors).mapOrThrow(result)); + temp.add(TenantConfigSQLHelper.TenantConfigRowMapper.getInstance(providers, firstFactors, + requiredSecondaryFactors).mapOrThrow(result)); } TenantConfig[] finalResult = new TenantConfig[temp.size()]; for (int i = 0; i < temp.size(); i++) { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java index ced73d6e..100194ea 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java @@ -35,7 +35,8 @@ public class ThirdPartyProviderClientSQLHelper { public static class TenantThirdPartyProviderClientRowMapper implements RowMapper { - public static final ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper INSTANCE = new ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper(); + public static final ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper INSTANCE = + new ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper(); private TenantThirdPartyProviderClientRowMapper() { } @@ -77,37 +78,48 @@ public ThirdPartyConfig.ProviderClient map(ResultSet result) throws StorageQuery } } - public static HashMap>> selectAll(Start start) + public static HashMap>> selectAll( + Start start) throws SQLException, StorageQueryException { HashMap>> providerClientsMap = new HashMap<>(); - String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, scope, force_pkce, additional_config FROM " - + getConfig(start).getTenantThirdPartyProviderClientsTable() + ";"; + String QUERY = + "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, " + + "client_secret, scope, force_pkce, additional_config FROM " + + getConfig(start).getTenantThirdPartyProviderClientsTable() + ";"; - execute(start, QUERY, pst -> {}, result -> { + execute(start, QUERY, pst -> { + }, result -> { while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); - ThirdPartyConfig.ProviderClient providerClient = ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper.getInstance().mapOrThrow(result); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); + ThirdPartyConfig.ProviderClient providerClient = + ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper.getInstance() + .mapOrThrow(result); if (!providerClientsMap.containsKey(tenantIdentifier)) { providerClientsMap.put(tenantIdentifier, new HashMap<>()); } - if(!providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { + if (!providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { providerClientsMap.get(tenantIdentifier).put(result.getString("third_party_id"), new HashMap<>()); } - providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).put(providerClient.clientType, providerClient); + providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")) + .put(providerClient.clientType, providerClient); } return null; }); return providerClientsMap; } - public static void create(Start start, Connection sqlCon, TenantConfig tenantConfig, ThirdPartyConfig.Provider provider, ThirdPartyConfig.ProviderClient providerClient) + public static void create(Start start, Connection sqlCon, TenantConfig tenantConfig, + ThirdPartyConfig.Provider provider, ThirdPartyConfig.ProviderClient providerClient) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getTenantThirdPartyProviderClientsTable() - + "(connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, scope, force_pkce, additional_config)" + + + "(connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, " + + "scope, force_pkce, additional_config)" + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; Array scopeArray; diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java index 3f2b6645..321f80d5 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java @@ -40,7 +40,8 @@ private TenantThirdPartyProviderRowMapper(ThirdPartyConfig.ProviderClient[] clie this.clients = clients; } - public static ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper getInstance(ThirdPartyConfig.ProviderClient[] clients) { + public static ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper getInstance( + ThirdPartyConfig.ProviderClient[] clients) { return new ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper(clients); } @@ -82,21 +83,36 @@ public ThirdPartyConfig.Provider map(ResultSet result) throws StorageQueryExcept } } - public static HashMap> selectAll(Start start, HashMap>> providerClientsMap) + public static HashMap> selectAll(Start start, + HashMap>> providerClientsMap) throws SQLException, StorageQueryException { HashMap> providerMap = new HashMap<>(); - String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, name, authorization_endpoint, authorization_endpoint_query_params, token_endpoint, token_endpoint_body_params, user_info_endpoint, user_info_endpoint_query_params, user_info_endpoint_headers, jwks_uri, oidc_discovery_endpoint, require_email, user_info_map_from_id_token_payload_user_id, user_info_map_from_id_token_payload_email, user_info_map_from_id_token_payload_email_verified, user_info_map_from_user_info_endpoint_user_id, user_info_map_from_user_info_endpoint_email, user_info_map_from_user_info_endpoint_email_verified FROM " - + getConfig(start).getTenantThirdPartyProvidersTable() + ";"; + String QUERY = + "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, name, authorization_endpoint, " + + "authorization_endpoint_query_params, token_endpoint, token_endpoint_body_params, " + + "user_info_endpoint, user_info_endpoint_query_params, user_info_endpoint_headers, jwks_uri, " + + "oidc_discovery_endpoint, require_email, user_info_map_from_id_token_payload_user_id, " + + "user_info_map_from_id_token_payload_email, " + + "user_info_map_from_id_token_payload_email_verified, " + + "user_info_map_from_user_info_endpoint_user_id, user_info_map_from_user_info_endpoint_email, " + + "user_info_map_from_user_info_endpoint_email_verified FROM " + + getConfig(start).getTenantThirdPartyProvidersTable() + ";"; - execute(start, QUERY, pst -> {}, result -> { + execute(start, QUERY, pst -> { + }, result -> { while (result.next()) { - TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")); + TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), + result.getString("app_id"), result.getString("tenant_id")); ThirdPartyConfig.ProviderClient[] clients = null; - if (providerClientsMap.containsKey(tenantIdentifier) && providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { - clients = providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).values().toArray(new ThirdPartyConfig.ProviderClient[0]); + if (providerClientsMap.containsKey(tenantIdentifier) && + providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) { + clients = providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).values() + .toArray(new ThirdPartyConfig.ProviderClient[0]); } - ThirdPartyConfig.Provider provider = ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper.getInstance(clients).mapOrThrow(result); + ThirdPartyConfig.Provider provider = + ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper.getInstance( + clients).mapOrThrow(result); if (!providerMap.containsKey(tenantIdentifier)) { providerMap.put(tenantIdentifier, new HashMap<>()); @@ -108,10 +124,19 @@ public static HashMap { pst.setString(1, tenantConfig.tenantIdentifier.getConnectionUriDomain()); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java b/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java index 6891f739..755b6720 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java @@ -662,9 +662,12 @@ public void testAllConfigsHaveAnAnnotation() throws Exception { continue; } - if (!(field.isAnnotationPresent(UserPoolProperty.class) || field.isAnnotationPresent(ConnectionPoolProperty.class) || field.isAnnotationPresent( + if (!(field.isAnnotationPresent(UserPoolProperty.class) || + field.isAnnotationPresent(ConnectionPoolProperty.class) || field.isAnnotationPresent( NotConflictingWithinUserPool.class))) { - fail(field.getName() + " does not have UserPoolProperty, ConnectionPoolProperty or NotConflictingWithinUserPool annotation"); + fail(field.getName() + + " does not have UserPoolProperty, ConnectionPoolProperty or NotConflictingWithinUserPool " + + "annotation"); } } } diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java index 470e6ce0..2a8aa753 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java @@ -154,14 +154,15 @@ public void testDownTimeWhenChangingConnectionPoolSize() throws Exception { try { TenantIdentifier t1 = new TenantIdentifier(null, null, "t1"); Storage t1Storage = (StorageLayer.getStorage(t1, process.getProcess())); - ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid"+ finalI, "user" + + ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid" + finalI, "user" + finalI + "@example.com"); if (firstErrorTime.get() != -1 && successAfterErrorTime.get() == -1) { successAfterErrorTime.set(System.currentTimeMillis()); } } catch (StorageQueryException e) { - if (e.getMessage().contains("Connection is closed") || e.getMessage().contains("has been closed")) { + if (e.getMessage().contains("Connection is closed") || + e.getMessage().contains("has been closed")) { if (firstErrorTime.get() == -1) { firstErrorTime.set(System.currentTimeMillis()); } @@ -355,7 +356,7 @@ public void testIdleConnectionTimeout() throws Exception { try { TenantIdentifier t1 = new TenantIdentifier(null, null, "t1"); Storage t1Storage = (StorageLayer.getStorage(t1, process.getProcess())); - ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid"+ finalI, "user" + + ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid" + finalI, "user" + finalI + "@example.com"); } catch (StorageQueryException e) { diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java index 22fe29bb..f86faadf 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java @@ -263,9 +263,11 @@ public void testCodeCreationRapidlyWithDifferentEmails() throws Exception { System.out.println("Durations: " + durations.toString()); assertNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); assertNotNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); assert (pass.get()); @@ -627,7 +629,8 @@ public void testLinkAccountsInParallel() throws Exception { for (int i = 0; i < 3000; i++) { es.execute(() -> { try { - AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), user1.getSupertokensUserId()); + AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), + user1.getSupertokensUserId()); AuthRecipe.unlinkAccounts(process.getProcess(), user2.getSupertokensUserId()); } catch (Exception e) { if (e.getMessage().toLowerCase().contains("the transaction might succeed if retried")) { @@ -642,9 +645,11 @@ public void testLinkAccountsInParallel() throws Exception { assert (pass.get()); assertNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); assertNotNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); @@ -685,9 +690,11 @@ public void testCreatePrimaryInParallel() throws Exception { assert (pass.get()); assertNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED)); assertNotNull(process - .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); + .checkOrWaitForEventInPlugin( + io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND)); process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java b/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java index 996ed4d2..e675c765 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java @@ -52,7 +52,7 @@ public void beforeEach() { public void testLogLevelNoneOutput() throws Exception { { Utils.setValueInConfig("log_level", "NONE"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -96,7 +96,7 @@ public void testLogLevelNoneOutput() throws Exception { public void testLogLevelErrorOutput() throws Exception { { Utils.setValueInConfig("log_level", "ERROR"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -150,7 +150,7 @@ public void testLogLevelErrorOutput() throws Exception { public void testLogLevelWarnOutput() throws Exception { { Utils.setValueInConfig("log_level", "WARN"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -204,7 +204,7 @@ public void testLogLevelWarnOutput() throws Exception { public void testLogLevelInfoOutput() throws Exception { { Utils.setValueInConfig("log_level", "INFO"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -258,7 +258,7 @@ public void testLogLevelInfoOutput() throws Exception { public void testLogLevelDebugOutput() throws Exception { { Utils.setValueInConfig("log_level", "DEBUG"); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java index 8aeddc0e..52602c2c 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java @@ -64,7 +64,7 @@ public void beforeEach() { @Test public void defaultLogging() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; StorageLayer.close(); TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); @@ -108,7 +108,7 @@ public void defaultLogging() throws Exception { @Test public void customLogging() throws Exception { try { - String[] args = { "../" }; + String[] args = {"../"}; Utils.setValueInConfig("info_log_path", "\"tempLogging/info.log\""); Utils.setValueInConfig("error_log_path", "\"tempLogging/error.log\""); @@ -159,7 +159,7 @@ public void customLogging() throws Exception { @Test public void confirmLoggerClosed() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; StorageLayer.close(); TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); @@ -187,7 +187,7 @@ public void confirmLoggerClosed() throws Exception { @Test public void testStandardOutLoggingWithNullStr() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; ByteArrayOutputStream stdOutput = new ByteArrayOutputStream(); ByteArrayOutputStream errorOutput = new ByteArrayOutputStream(); @@ -223,7 +223,7 @@ public void testStandardOutLoggingWithNullStr() throws Exception { @Test public void testStandardOutLoggingWithNull() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; ByteArrayOutputStream stdOutput = new ByteArrayOutputStream(); ByteArrayOutputStream errorOutput = new ByteArrayOutputStream(); @@ -260,7 +260,7 @@ public void testStandardOutLoggingWithNull() throws Exception { @Test public void confirmHikariLoggerClosedOnlyWhenProcessEnds() throws Exception { StorageLayer.close(); - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false); FeatureFlagTestContent.getInstance(process.getProcess()) .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY}); @@ -314,7 +314,7 @@ public void confirmHikariLoggerClosedOnlyWhenProcessEnds() throws Exception { @Test public void testDBPasswordMaskingOnDBConnectionFailUsingConnectionUri() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; String dbUser = "db_user"; String dbPassword = "db_password"; @@ -346,7 +346,7 @@ public void testDBPasswordMaskingOnDBConnectionFailUsingConnectionUri() throws E @Test public void testDBPasswordMaskingOnDBConnectionFailUsingCredentials() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; String dbUser = "db_user"; String dbPassword = "db_password"; @@ -380,7 +380,7 @@ public void testDBPasswordMaskingOnDBConnectionFailUsingCredentials() throws Exc @Test public void testDBPasswordMasking() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; ByteArrayOutputStream stdOutput = new ByteArrayOutputStream(); ByteArrayOutputStream errorOutput = new ByteArrayOutputStream(); @@ -415,7 +415,7 @@ public void testDBPasswordMasking() throws Exception { @Test public void testDBPasswordIsNotLoggedWhenProcessStartsEnds() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; Utils.setValueInConfig("error_log_path", "null"); Utils.setValueInConfig("info_log_path", "null"); @@ -478,7 +478,7 @@ public void testDBPasswordIsNotLoggedWhenProcessStartsEnds() throws Exception { @Test public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; Utils.setValueInConfig("error_log_path", "null"); Utils.setValueInConfig("info_log_path", "null"); @@ -504,8 +504,8 @@ public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception { Main main = process.getProcess(); FeatureFlagTestContent.getInstance(main) - .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[] { - EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY }); + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); JsonObject config = new JsonObject(); TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null); @@ -547,8 +547,8 @@ public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception { Main main = process.getProcess(); FeatureFlagTestContent.getInstance(main) - .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[] { - EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY }); + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null); JsonObject config = new JsonObject(); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java index 37346a11..5f55bcf1 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java @@ -107,7 +107,8 @@ private void createEmailPasswordUsers(Main main) throws Exception { String userId = io.supertokens.utils.Utils.getUUID(); long timeJoined = System.currentTimeMillis(); - storage.signUp(TenantIdentifier.BASE_TENANT, userId, "eptest" + finalI + "@example.com", combinedPasswordHash, + storage.signUp(TenantIdentifier.BASE_TENANT, userId, "eptest" + finalI + "@example.com", + combinedPasswordHash, timeJoined); synchronized (lock) { allUserIds.add(userId); @@ -116,7 +117,7 @@ private void createEmailPasswordUsers(Main main) throws Exception { throw new RuntimeException(e); } if (finalI % 10000 == 9999) { - System.out.println("Created " + ((finalI +1)) + " users"); + System.out.println("Created " + ((finalI + 1)) + " users"); } }); } @@ -137,7 +138,8 @@ private void createPasswordlessUsersWithEmail(Main main) throws Exception { String userId = io.supertokens.utils.Utils.getUUID(); long timeJoined = System.currentTimeMillis(); try { - storage.createUser(TenantIdentifier.BASE_TENANT, userId, "pltest" + finalI + "@example.com", null, timeJoined); + storage.createUser(TenantIdentifier.BASE_TENANT, userId, "pltest" + finalI + "@example.com", null, + timeJoined); synchronized (lock) { allUserIds.add(userId); } @@ -146,7 +148,7 @@ private void createPasswordlessUsersWithEmail(Main main) throws Exception { } if (finalI % 10000 == 9999) { - System.out.println("Created " + ((finalI +1)) + " users"); + System.out.println("Created " + ((finalI + 1)) + " users"); } }); } @@ -176,7 +178,7 @@ private void createPasswordlessUsersWithPhone(Main main) throws Exception { } if (finalI % 10000 == 9999) { - System.out.println("Created " + ((finalI +1)) + " users"); + System.out.println("Created " + ((finalI + 1)) + " users"); } }); } @@ -198,7 +200,8 @@ private void createThirdpartyUsers(Main main) throws Exception { long timeJoined = System.currentTimeMillis(); try { - storage.signUp(TenantIdentifier.BASE_TENANT, userId, "tptest" + finalI + "@example.com", new LoginMethod.ThirdParty("google", "googleid" + finalI), timeJoined ); + storage.signUp(TenantIdentifier.BASE_TENANT, userId, "tptest" + finalI + "@example.com", + new LoginMethod.ThirdParty("google", "googleid" + finalI), timeJoined); synchronized (lock) { allUserIds.add(userId); } @@ -207,7 +210,7 @@ private void createThirdpartyUsers(Main main) throws Exception { } if (finalI % 10000 == 9999) { - System.out.println("Created " + (finalI +1) + " users"); + System.out.println("Created " + (finalI + 1) + " users"); } }); } @@ -646,7 +649,8 @@ private void sanityCheckAPIs(Main main) throws Exception { JsonArray userRolesArr = response.getAsJsonArray("roles"); assertEquals(1, userRolesArr.size()); assertTrue( - userRolesArr.get(0).getAsString().equals("admin") || userRolesArr.get(0).getAsString().equals("user") + userRolesArr.get(0).getAsString().equals("admin") || + userRolesArr.get(0).getAsString().equals("user") ); } @@ -791,7 +795,8 @@ private void measureOperations(Main main) throws Exception { int finalI = i; es.execute(() -> { try { - ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, "twitter" + finalI + "@example.com"); + ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, + "twitter" + finalI + "@example.com"); } catch (Exception e) { errorCount.incrementAndGet(); throw new RuntimeException(e); @@ -817,7 +822,8 @@ private void measureOperations(Main main) throws Exception { int finalI = i; es.execute(() -> { try { - ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, "twitter" + finalI + "@example.com"); + ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, + "twitter" + finalI + "@example.com"); } catch (Exception e) { errorCount.incrementAndGet(); throw new RuntimeException(e); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java b/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java index 21064f15..21a86f60 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java @@ -56,14 +56,14 @@ public void beforeEach() { @Test public void testMatchConfigPropertiesDescription() throws Exception { - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); // Skipping postgresql_config_version because it doesn't // have a description in the config.yaml file - String[] ignoredProperties = { "postgresql_config_version" }; + String[] ignoredProperties = {"postgresql_config_version"}; // Match the descriptions in the config.yaml file with the descriptions in the // CoreConfig class @@ -144,8 +144,10 @@ private void matchYamlAndConfigDescriptions(String path, String[] ignoreProperti } String descriptionInConfig = field.getAnnotation(DashboardInfo.class).description(); - descriptionInConfig = "(DIFFERENT_ACROSS_TENANTS" + (field.getAnnotation(DashboardInfo.class).isOptional() ? " | OPTIONAL" : " | COMPULSORY") - + (field.getAnnotation(DashboardInfo.class).isOptional() ? " | Default: " + field.getAnnotation(DashboardInfo.class).defaultValue() : "") + descriptionInConfig = "(DIFFERENT_ACROSS_TENANTS" + + (field.getAnnotation(DashboardInfo.class).isOptional() ? " | OPTIONAL" : " | COMPULSORY") + + (field.getAnnotation(DashboardInfo.class).isOptional() ? + " | Default: " + field.getAnnotation(DashboardInfo.class).defaultValue() : "") + ") " + valueInfo + " " + descriptionInConfig; diff --git a/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java index 8f6d1699..e2ee74f2 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java @@ -115,13 +115,18 @@ public void testLinkedAccountUser() throws Exception { AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "test1@example.com", "password"); Thread.sleep(50); - AuthRecipeUserInfo user2 = ThirdParty.signInUp(process.getProcess(), "google", "googleid", "test2@example.com").user; + AuthRecipeUserInfo user2 = ThirdParty.signInUp(process.getProcess(), "google", "googleid", + "test2@example.com").user; Thread.sleep(50); - Passwordless.CreateCodeResponse code1 = Passwordless.createCode(process.getProcess(), "test3@example.com", null, null, null); - AuthRecipeUserInfo user3 = Passwordless.consumeCode(process.getProcess(), code1.deviceId, code1.deviceIdHash, code1.userInputCode, null).user; + Passwordless.CreateCodeResponse code1 = Passwordless.createCode(process.getProcess(), "test3@example.com", null, + null, null); + AuthRecipeUserInfo user3 = Passwordless.consumeCode(process.getProcess(), code1.deviceId, code1.deviceIdHash, + code1.userInputCode, null).user; Thread.sleep(50); - Passwordless.CreateCodeResponse code2 = Passwordless.createCode(process.getProcess(), null, "+919876543210", null, null); - AuthRecipeUserInfo user4 = Passwordless.consumeCode(process.getProcess(), code2.deviceId, code2.deviceIdHash, code2.userInputCode, null).user; + Passwordless.CreateCodeResponse code2 = Passwordless.createCode(process.getProcess(), null, "+919876543210", + null, null); + AuthRecipeUserInfo user4 = Passwordless.consumeCode(process.getProcess(), code2.deviceId, code2.deviceIdHash, + code2.userInputCode, null).user; AuthRecipe.createPrimaryUser(process.getProcess(), user3.getSupertokensUserId()); AuthRecipe.linkAccounts(process.getProcess(), user1.getSupertokensUserId(), user3.getSupertokensUserId()); @@ -135,8 +140,9 @@ public void testLinkedAccountUser() throws Exception { user4.getSupertokensUserId() }; - for (String userId : userIds){ - AuthRecipeUserInfo primaryUser = ((AuthRecipeStorage) StorageLayer.getStorage(process.getProcess())).getPrimaryUserById( + for (String userId : userIds) { + AuthRecipeUserInfo primaryUser = ((AuthRecipeStorage) StorageLayer.getStorage( + process.getProcess())).getPrimaryUserById( new AppIdentifier(null, null), userId); assertEquals(user3.getSupertokensUserId(), primaryUser.getSupertokensUserId()); assertEquals(4, primaryUser.loginMethods.length); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java b/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java index 51673061..eb135240 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java @@ -86,11 +86,12 @@ public void testThatTenantCannotSetDatabaseRelatedConfigIfSuperTokensSaaSSecretI try { JsonObject j = new JsonObject(); j.addProperty(PROTECTED_DB_CONFIG[i], ""); - Multitenancy.addNewOrUpdateAppOrTenant(process.main, new TenantConfig(new TenantIdentifier(null, null, "t1"), new EmailPasswordConfig(false), - new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), - new PasswordlessConfig(false), - null, null, - j), true); + Multitenancy.addNewOrUpdateAppOrTenant(process.main, + new TenantConfig(new TenantIdentifier(null, null, "t1"), new EmailPasswordConfig(false), + new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), + new PasswordlessConfig(false), + null, null, + j), true); fail(); } catch (BadPermissionException e) { assertEquals(e.getMessage(), "Not allowed to modify DB related configs."); @@ -162,12 +163,13 @@ public void testThatTenantCanSetDatabaseRelatedConfigIfSuperTokensSaaSSecretIsNo } else if (PROTECTED_DB_CONFIG_VALUES[i] instanceof Integer) { j.addProperty(PROTECTED_DB_CONFIG[i], (Integer) PROTECTED_DB_CONFIG_VALUES[i]); } - Multitenancy.addNewOrUpdateAppOrTenant(process.main, new TenantConfig(new TenantIdentifier(null, null, "t1"), - new EmailPasswordConfig(false), - new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), - new PasswordlessConfig(false), - null, null, - j), false); + Multitenancy.addNewOrUpdateAppOrTenant(process.main, + new TenantConfig(new TenantIdentifier(null, null, "t1"), + new EmailPasswordConfig(false), + new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]), + new PasswordlessConfig(false), + null, null, + j), false); } JsonObject coreConfig = new JsonObject(); @@ -224,7 +226,8 @@ public void testThatTenantCannotGetDatabaseRelatedConfigIfSuperTokensSaaSSecretI { JsonObject response = HttpRequestForTesting.sendJsonRequest(process.getProcess(), "", - HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, "/recipe/multitenancy/tenant/list"), + HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, + "/recipe/multitenancy/tenant/list"), null, 1000, 1000, null, SemVer.v3_0.get(), "GET", apiKey, "multitenancy"); @@ -245,7 +248,8 @@ public void testThatTenantCannotGetDatabaseRelatedConfigIfSuperTokensSaaSSecretI { JsonObject response = HttpRequestForTesting.sendJsonRequest(process.getProcess(), "", - HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, "/recipe/multitenancy/tenant/list"), + HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, + "/recipe/multitenancy/tenant/list"), null, 1000, 1000, null, SemVer.v3_0.get(), "GET", saasSecret, "multitenancy"); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java b/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java index f215a4c0..81f989c8 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java @@ -45,7 +45,7 @@ public void beforeEach() { @Test public void checkingCreationOfNewTable() throws InterruptedException { - String[] args = { "../" }; + String[] args = {"../"}; TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java b/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java index 5dec4454..4c3f6e25 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java @@ -75,8 +75,9 @@ public void testThatMainThreadIsSameThroughout() throws Exception { JsonObject requestBody = new JsonObject(); requestBody.addProperty("appId", "a1"); requestBody.add("coreConfig", config); - HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", "http://localhost:3567/recipe/multitenancy/app", - requestBody, 1000, 2500, null, "3.0", "multitenancy"); + HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/multitenancy/app", + requestBody, 1000, 2500, null, "3.0", "multitenancy"); Storage storage2 = StorageLayer.getStorage(new TenantIdentifier(null, "a1", null), process.getProcess()); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java b/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java index 5ca0aa3e..52e1f7cf 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java @@ -62,7 +62,8 @@ private static boolean isJsonValid(String jsonInString) { @SuppressWarnings("unchecked") public static T sendGETRequest(Main main, String requestID, String url, Map params, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, + String rid) throws IOException, HttpResponseException { StringBuilder paramBuilder = new StringBuilder(); @@ -198,101 +199,107 @@ public static T sendJsonRequest(Main main, String requestID, String url, Jso } public static T sendJsonPOSTRequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "POST", null, rid); } public static T sendJsonPOSTRequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String apiKey, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String apiKey, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "POST", apiKey, rid); } public static T sendJsonPUTRequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "PUT", null, rid); } public static T sendJsonDELETERequest(Main main, String requestID, String url, JsonElement requestBody, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + int connectionTimeoutMS, int readTimeoutMS, Integer version, + String cdiVersion, String rid) throws IOException, HttpResponseException { return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version, cdiVersion, "DELETE", null, rid); } @SuppressWarnings("unchecked") - public static T sendJsonDELETERequestWithQueryParams(Main main, String requestID, String url, Map params, - int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid) + public static T sendJsonDELETERequestWithQueryParams(Main main, String requestID, String url, + Map params, + int connectionTimeoutMS, int readTimeoutMS, + Integer version, String cdiVersion, String rid) throws IOException, HttpResponseException { - StringBuilder paramBuilder = new StringBuilder(); + StringBuilder paramBuilder = new StringBuilder(); - if (params != null) { - for (Map.Entry entry : params.entrySet()) { - paramBuilder.append(entry.getKey()).append("=") - .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("&"); - } - } - String paramsStr = paramBuilder.toString(); - if (!paramsStr.equals("")) { - paramsStr = paramsStr.substring(0, paramsStr.length() - 1); - url = url + "?" + paramsStr; + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + paramBuilder.append(entry.getKey()).append("=") + .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("&"); + } + } + String paramsStr = paramBuilder.toString(); + if (!paramsStr.equals("")) { + paramsStr = paramsStr.substring(0, paramsStr.length() - 1); + url = url + "?" + paramsStr; + } + URL obj = getURL(main, requestID, url); + InputStream inputStream = null; + HttpURLConnection con = null; + + try { + con = (HttpURLConnection) obj.openConnection(); + con.setRequestMethod("DELETE"); + con.setConnectTimeout(connectionTimeoutMS); + con.setReadTimeout(readTimeoutMS + 1000); + if (version != null) { + con.setRequestProperty("api-version", version + ""); + } + if (cdiVersion != null) { + con.setRequestProperty("cdi-version", cdiVersion); + } + if (rid != null) { + con.setRequestProperty("rId", rid); + } + + int responseCode = con.getResponseCode(); + + if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { + inputStream = con.getInputStream(); + } else { + inputStream = con.getErrorStream(); + } + + StringBuilder response = new StringBuilder(); + try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); } - URL obj = getURL(main, requestID, url); - InputStream inputStream = null; - HttpURLConnection con = null; - - try { - con = (HttpURLConnection) obj.openConnection(); - con.setRequestMethod("DELETE"); - con.setConnectTimeout(connectionTimeoutMS); - con.setReadTimeout(readTimeoutMS + 1000); - if (version != null) { - con.setRequestProperty("api-version", version + ""); - } - if (cdiVersion != null) { - con.setRequestProperty("cdi-version", cdiVersion); - } - if (rid != null) { - con.setRequestProperty("rId", rid); - } - - int responseCode = con.getResponseCode(); - - if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { - inputStream = con.getInputStream(); - } else { - inputStream = con.getErrorStream(); - } - - StringBuilder response = new StringBuilder(); - try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - String inputLine; - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - } - if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { - if (!isJsonValid(response.toString())) { - return (T) response.toString(); - } - return (T) (new JsonParser().parse(response.toString())); - } - throw new HttpResponseException(responseCode, response.toString()); - } finally { - if (inputStream != null) { - inputStream.close(); - } - - if (con != null) { - con.disconnect(); - } + } + if (responseCode < STATUS_CODE_ERROR_THRESHOLD) { + if (!isJsonValid(response.toString())) { + return (T) response.toString(); } + return (T) (new JsonParser().parse(response.toString())); + } + throw new HttpResponseException(responseCode, response.toString()); + } finally { + if (inputStream != null) { + inputStream.close(); + } + + if (con != null) { + con.disconnect(); + } } + } public static String getMultitenantUrl(TenantIdentifier tenantIdentifier, String path) { StringBuilder sb = new StringBuilder(); diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java index 77204865..257fb60e 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java @@ -328,7 +328,8 @@ public void mergingTenantWithBaseConfigWithConflictingConfigsThrowsError() fail(); } catch (InvalidConfigException e) { assertEquals(e.getMessage(), - "You cannot set different values for postgresql_thirdparty_users_table_name for the same user pool"); + "You cannot set different values for postgresql_thirdparty_users_table_name for the same user " + + "pool"); } process.kill(); @@ -805,7 +806,8 @@ public void testTenantCreationAndThenDbDownDbThrowsErrorInRecipesAndDoesntAffect tenantConfigJson); StorageLayer.getMultitenancyStorage(process.getProcess()).createTenant(tenantConfig); - MultitenancyHelper.getInstance(process.getProcess()).refreshTenantsInCoreBasedOnChangesInCoreConfigOrIfTenantListChanged(true); + MultitenancyHelper.getInstance(process.getProcess()) + .refreshTenantsInCoreBasedOnChangesInCoreConfigOrIfTenantListChanged(true); try { EmailPassword.signIn(tid, (StorageLayer.getStorage(tid, process.getProcess())), diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java index b8635224..801737fd 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java @@ -107,7 +107,8 @@ public void testThatCUDRecoversWhenItFailsToAddEntryDuringCreation() throws Exce assertEquals(2, allTenants.length); // should have the new CUD try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -117,14 +118,16 @@ public void testThatCUDRecoversWhenItFailsToAddEntryDuringCreation() throws Exce MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false; // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } @Test public void testThatCUDRecoversWhenTheDbIsDownDuringCreationButDbComesUpLater() throws Exception { Start start = ((Start) StorageLayer.getBaseStorage(process.getProcess())); try { - update(start, "DROP DATABASE st5000;", pst -> {}); + update(start, "DROP DATABASE st5000;", pst -> { + }); } catch (Exception e) { // ignore } @@ -147,24 +150,30 @@ public void testThatCUDRecoversWhenTheDbIsDownDuringCreationButDbComesUpLater() fail(); } catch (StorageQueryException e) { // ignore - assertEquals("java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage()); + assertEquals( + "java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to " + + "initialize pool: FATAL: database \"st5000\" does not exist", + e.getMessage()); } TenantConfig[] allTenants = MultitenancyHelper.getInstance(process.getProcess()).getAllTenants(); assertEquals(2, allTenants.length); // should have the new CUD try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore assertTrue(e.getMessage().contains("Internal Error")); // db is still down } - update(start, "CREATE DATABASE st5000;", pst -> {}); + update(start, "CREATE DATABASE st5000;", pst -> { + }); // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } @Test @@ -260,7 +269,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddEntry MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false; // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } @Test @@ -291,7 +301,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenTenantEntryIsInconsistentIn assertEquals(2, allTenants.length); // should have the new CUD Start start = (Start) StorageLayer.getBaseStorage(process.getProcess()); - update(start, "DELETE FROM apps;", pst -> {}); + update(start, "DELETE FROM apps;", pst -> { + }); process.kill(false); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); @@ -306,13 +317,15 @@ public void testThatCoreDoesNotCrashDuringStartupWhenTenantEntryIsInconsistentIn MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false; // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); Session.createNewSession(process.getProcess(), "userid", new JsonObject(), new JsonObject()); } @Test - public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntryInTheBaseTenantStorage() throws Exception { + public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntryInTheBaseTenantStorage() + throws Exception { JsonObject coreConfig = new JsonObject(); TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null); @@ -354,7 +367,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntry assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); Session.createNewSession( new TenantIdentifier(null, "a1", null), @@ -364,7 +378,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntry } @Test - public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenantEntryInTargetStorageWithLoadOnlyCUDConfig() throws Exception { + public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenantEntryInTargetStorageWithLoadOnlyCUDConfig() + throws Exception { JsonObject coreConfig = new JsonObject(); TenantIdentifier tenantIdentifier = new TenantIdentifier("127.0.0.1", null, null); @@ -442,10 +457,12 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan assertEquals(2, allTenants.length); // should have the new CUD // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); try { - tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -465,9 +482,11 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan assertEquals(2, allTenants.length); // should have the new CUD // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -488,7 +507,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throws Exception { Start start = ((Start) StorageLayer.getBaseStorage(process.getProcess())); try { - update(start, "DROP DATABASE st5000;", pst -> {}); + update(start, "DROP DATABASE st5000;", pst -> { + }); } catch (Exception e) { // ignore } @@ -511,14 +531,18 @@ public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throw fail(); } catch (StorageQueryException e) { // ignore - assertEquals("java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage()); + assertEquals( + "java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to " + + "initialize pool: FATAL: database \"st5000\" does not exist", + e.getMessage()); } TenantConfig[] allTenants = MultitenancyHelper.getInstance(process.getProcess()).getAllTenants(); assertEquals(2, allTenants.length); // should have the new CUD try { - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); fail(); } catch (HttpResponseException e) { // ignore @@ -537,13 +561,16 @@ public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throw // the process should start successfully even though the db is down start = ((Start) StorageLayer.getBaseStorage(process.getProcess())); - update(start, "CREATE DATABASE st5000;", pst -> {}); + update(start, "CREATE DATABASE st5000;", pst -> { + }); // this should succeed now - tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0); + tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", + "test@example.com", process.getProcess(), SemVer.v5_0); } - public static JsonObject tpSignInUpAndGetResponse(TenantIdentifier tenantIdentifier, String thirdPartyId, String thirdPartyUserId, String email, Main main, SemVer version) + public static JsonObject tpSignInUpAndGetResponse(TenantIdentifier tenantIdentifier, String thirdPartyId, + String thirdPartyUserId, String email, Main main, SemVer version) throws HttpResponseException, IOException { JsonObject emailObject = new JsonObject(); emailObject.addProperty("id", email); From dc0d4ad247a97c1bdd22300e3cc46157f01ac456 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 9 Jul 2024 15:50:17 +0530 Subject: [PATCH 05/45] feat: multitenancy dashboard (#220) * fix: providers non null * fix: changelog * fix: build fix * reformats code * fix: changelog * fix: reformat --------- Co-authored-by: rishabhpoddar --- .idea/gradle.xml | 28 +++++++++---------- .idea/misc.xml | 2 +- .idea/vcs.xml | 6 ++-- CHANGELOG.md | 9 +++--- .../queries/MultitenancyQueries.java | 7 +++-- .../multitenancy/TenantConfigSQLHelper.java | 10 +++---- .../test/multitenancy/StorageLayerTest.java | 15 ++++++---- 7 files changed, 39 insertions(+), 38 deletions(-) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 14ff0eb7..889d0053 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,18 +1,18 @@ - - - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 6ed36dd3..3338eab6 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1ddf..9db25eef 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ffb3c2ee..d7b13708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,18 +9,17 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [7.1.0] +- Compatible with plugin interface version 6.2 - Adds implementation for a new method `getConfigFieldsInfo` to fetch the plugin config fields. -- Adds `null` state for `firstFactors` and `providers` by adding `is_first_factors_null` - and `is_third_party_providers_null` fields in `tenant_configs` table +- Adds `DashboardInfo` annotations to the config properties in `PostgreSQLConfig` +- Adds `null` state for `firstFactors` by adding `is_first_factors_null` field in `tenant_configs` table. The value of + this column is only applicable when there are no entries in the `tenant_first_factors` table for the tenant. ### Migration ```sql ALTER TABLE tenant_configs ADD COLUMN IF NOT EXISTS is_first_factors_null BOOLEAN DEFAULT TRUE; -ALTER TABLE tenant_configs ADD COLUMN IF NOT EXISTS is_third_party_providers_null BOOLEAN DEFAULT TRUE; - ALTER TABLE tenant_configs ALTER COLUMN is_first_factors_null DROP DEFAULT; -ALTER TABLE tenant_configs ALTER COLUMN is_third_party_providers_null DROP DEFAULT; ``` ## [7.0.1] - 2024-04-17 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 a6c9b749..b0c7ffe1 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java @@ -18,7 +18,9 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; -import io.supertokens.pluginInterface.multitenancy.*; +import io.supertokens.pluginInterface.multitenancy.TenantConfig; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.multitenancy.ThirdPartyConfig; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; @@ -32,8 +34,8 @@ import java.sql.SQLException; import java.util.HashMap; -import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; import static io.supertokens.storage.postgresql.config.Config.getConfig; public class MultitenancyQueries { @@ -52,7 +54,6 @@ static String getQueryToCreateTenantConfigsTable(Start start) { + "passwordless_enabled BOOLEAN," + "third_party_enabled BOOLEAN," + "is_first_factors_null BOOLEAN," - + "is_third_party_providers_null BOOLEAN," + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)" + ");"; diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java index 3a57f9aa..79b4fb18 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java @@ -56,14 +56,13 @@ public static TenantConfigSQLHelper.TenantConfigRowMapper getInstance(ThirdParty public TenantConfig map(ResultSet result) throws StorageQueryException { try { boolean isFirstFactorsNull = result.getBoolean("is_first_factors_null"); - boolean isThirdPartyProvidersNull = result.getBoolean("is_third_party_providers_null"); return new TenantConfig( new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id")), new EmailPasswordConfig(result.getBoolean("email_password_enabled")), new ThirdPartyConfig( result.getBoolean("third_party_enabled"), - providers.length == 0 && isThirdPartyProvidersNull ? null : providers), + providers), new PasswordlessConfig(result.getBoolean("passwordless_enabled")), firstFactors.length == 0 && isFirstFactorsNull ? null : firstFactors, requiredSecondaryFactors.length == 0 ? null : requiredSecondaryFactors, @@ -82,7 +81,7 @@ public static TenantConfig[] selectAll(Start start, throws SQLException, StorageQueryException { String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, core_config," + " email_password_enabled, passwordless_enabled, third_party_enabled, " - + " is_first_factors_null, is_third_party_providers_null FROM " + + " is_first_factors_null FROM " + getConfig(start).getTenantConfigsTable() + ";"; TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> { @@ -121,8 +120,8 @@ public static void create(Start start, Connection sqlCon, TenantConfig tenantCon String QUERY = "INSERT INTO " + getConfig(start).getTenantConfigsTable() + "(connection_uri_domain, app_id, tenant_id, core_config," + " email_password_enabled, passwordless_enabled, third_party_enabled," - + " is_first_factors_null, is_third_party_providers_null)" - + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + " is_first_factors_null)" + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantConfig.tenantIdentifier.getConnectionUriDomain()); @@ -133,7 +132,6 @@ public static void create(Start start, Connection sqlCon, TenantConfig tenantCon pst.setBoolean(6, tenantConfig.passwordlessConfig.enabled); pst.setBoolean(7, tenantConfig.thirdPartyConfig.enabled); pst.setBoolean(8, tenantConfig.firstFactors == null); - pst.setBoolean(9, tenantConfig.thirdPartyConfig.providers == null); }); } diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java index 257fb60e..dc592a21 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java @@ -174,11 +174,12 @@ public void storageInstanceIsReusedAcrossTenants() StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess())); Assert.assertEquals( - Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()).getAccessTokenValidity(), + Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()) + .getAccessTokenValidityInMillis(), (long) 3600 * 1000); Assert.assertEquals(Config.getConfig(new TenantIdentifier(null, "abc", null), process.getProcess()) - .getAccessTokenValidity(), + .getAccessTokenValidityInMillis(), (long) 3601 * 1000); Assert.assertEquals( @@ -238,11 +239,12 @@ public void storageInstanceIsReusedAcrossTenantsComplex() StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess())); Assert.assertEquals( - Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()).getAccessTokenValidity(), + Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()) + .getAccessTokenValidityInMillis(), (long) 3600 * 1000); Assert.assertEquals(Config.getConfig(new TenantIdentifier(null, "abc", null), process.getProcess()) - .getAccessTokenValidity(), + .getAccessTokenValidityInMillis(), (long) 3601 * 1000); Assert.assertEquals( @@ -454,11 +456,12 @@ public void newStorageIsNotCreatedWhenSameTenantIsAdded() existingStorage); Assert.assertEquals( - Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()).getAccessTokenValidity(), + Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()) + .getAccessTokenValidityInMillis(), (long) 3600 * 1000); Assert.assertEquals(Config.getConfig(new TenantIdentifier(null, "abc", null), process.getProcess()) - .getAccessTokenValidity(), + .getAccessTokenValidityInMillis(), (long) 3601 * 1000); Assert.assertEquals( From 8d4c431bd634ab467cd621ad357d102fea12b415 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 10 Jul 2024 15:09:40 +0530 Subject: [PATCH 06/45] adding dev-v7.1.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.1.0.jar | Bin 223814 -> 223821 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.1.0.jar b/jar/postgresql-plugin-7.1.0.jar index a6fb21b003af1614614828daf54192a9f493cc3f..c409abbaac7e264776422ffb6cb0d745d2d056e0 100644 GIT binary patch delta 169089 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≫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 Yq8Bva%I*h+`$9UY0hQUeN^DZ{e|-Z9LI3~& From 50573f7a8135a523867203d7a4fb1f7a2d43e7fb Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 10 Jul 2024 20:23:12 +0530 Subject: [PATCH 07/45] fix: gradle.properties in tests (#221) --- .circleci/doOneMillionUsersTests.sh | 1 + .circleci/doTests.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/.circleci/doOneMillionUsersTests.sh b/.circleci/doOneMillionUsersTests.sh index ec82508d..2b4ca15d 100755 --- a/.circleci/doOneMillionUsersTests.sh +++ b/.circleci/doOneMillionUsersTests.sh @@ -93,6 +93,7 @@ do cd ../../ git clone git@github.com:supertokens/supertokens-root.git cd supertokens-root + rm gradle.properties update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 diff --git a/.circleci/doTests.sh b/.circleci/doTests.sh index 554e23ae..6aae469b 100755 --- a/.circleci/doTests.sh +++ b/.circleci/doTests.sh @@ -93,6 +93,7 @@ do cd ../../ git clone git@github.com:supertokens/supertokens-root.git cd supertokens-root + rm gradle.properties update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 From 8c9e074efb716bff5cc4ed8139f96507540ae32e Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 10 Jul 2024 20:46:05 +0530 Subject: [PATCH 08/45] adding dev-v7.1.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.1.0.jar | Bin 223821 -> 223821 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.1.0.jar b/jar/postgresql-plugin-7.1.0.jar index c409abbaac7e264776422ffb6cb0d745d2d056e0..bd1054b14787e8e934d2b78ec3751daa0538e40a 100644 GIT binary patch delta 1570 zcmY+DYfPI}7{@v1DR)Z75Xr{W@nPC+E(H~%K!~FkI!d{W`*o$|RwpoFzz8@Mov zz7_GE`Ku&W@J(v#WS6?HIgPtuJjp%3xX!n>Fvj5>Tl@>zUSdtaPkksA(UqRq&StYTKR-9?{DWz zI9$KIgEt|gz9P>kYX>-P(I4B*yT#UyAL2IQpBvzPqFwDVPMAt*802PnCkMGpcsHEp z+E7$<=Xee(o#%K!^homXMj_rk&pU;P8RrfmIxg^fA%1$FcZnqyPjIggp@<=cylEu78;agUd%@Etss;ZsQ8U@(Si_ek%vpMt%@`*Nj2^_S^ z`B!XIg}6Ld_Mgg?K2sjGApc38oG&AvUPt^zKAivy3&;ru3aA6r@1~EyUv|?$&{;@B z;DbUs0v;-&H^InaIuH7aX+OBNgkA$LmdJH&D3#|jP)a?>2balu|9=cGr=!T9DyLR3 zw1Nh}lNIz1xV@4Z!J$gI|F|l-pP?%053ZK%td@O#t(NQBTO-fuUJbp0@jZLwzSaBN zq-?emz6hn#P7d&MyFBdVTG?#4mfDbyc2E&m=b#2~#zA|)9ZsqM4?AVk`%XH5xUG(Q z!9VKce2#kRMEpa&jB^^O67huw*=K#DtRHEVm$Td`*HhCZU!v(I>W1IwqH=K5MH%2N H7q$EgkFI@D delta 1570 zcmY+DYfPI}7{`0gQ|_0Dh-72x_%O#dmx72r#I7ta5~do3?`kNMFZcxAxkW1m?##*hA#y~zQo%h;u&AwHq_(h zzZLPFg{vf1@J)K#RJXdXd9BAfk?dJmddRoFIL6@}TlxzveIs|q9szf)k8V<_+}_9v z#8~cMutut5pVNAZf|ESn%us)80^5PX{*lL6ELPz6CbBD05G^{k8@>I3r`diXwm-|R zz@sdsvr<&DGT4{l5LzQ!#J2s*X4W48(P3lvMW&^O?GhPpGaHLUsjQQ=2SZ%zVRs*g z_-vS^iw-O2m?;Js{bd%h1;RSVu8Pveo9sIw?*7OeLX_QN<3g<6W>b&CJo$i`#3ue; zV~0ekoAKE=lnn8FRFr;8;M1Zs^#u2d#oM>@zUSdtbn*|wAe0vVlsJ{I+W4d}AL!uA zI9$KIlQ$!yz9P>k>jyb*(I4yPZn3o!hq*=g=LdM7XjgZf6Q)uc2e}d6sX^`%-VJAY zR0t|b=XpLVUFUf~^hh4(O+uWxz`KNqncxl~Ixq4DA%1$FcZ(&KPI9jhAzofCde`~* z3E}zkV;(9ptA2h?9P985zb0;_@dkfOcz*wy+g`>hafm*;;Be}rujk0dcr830t z=F*E`xJmXuW}^Lw*GzN-9N10A!T3Dt14r^?J<3e|h)(q|~7R^&e^l=Ed3(d&r6D58^Kv6Y-)z)GEP4=R`S{{I+OLC26kT|sSN zNF@z`rz+_ka7Pt2fkRbt|8doFKSR~hA5 Date: Mon, 15 Jul 2024 11:40:29 +0530 Subject: [PATCH 09/45] adding dev-v7.1.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.1.0.jar | Bin 223821 -> 223821 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.1.0.jar b/jar/postgresql-plugin-7.1.0.jar index bd1054b14787e8e934d2b78ec3751daa0538e40a..c538f8a8f357bbfea3f20776779ebfa2a1a8b5d3 100644 GIT binary patch delta 1876 zcmYL|dr(wW9LMiDzXcI@Ny5kxGj-BHO%TgMQ5s+ul(#%~K@gS&7E0JLL`bDHN5e;h zw&`sIvIcPwl*g=f)&A%;>8MdTqcfFRj9O;>;aJU>FQ#+8=Qi!k?)~iV_x(N3xg!^C zBNuJ9+zhgWYFbE$7FKp^uWdHT$D$RpZDqGyn)q<}BR(X)b-q~A{tpn6y@%(Kd|Y}~ z#`&!*N&XFFvwn*N|AQb04EErUX8G&`qb!~E5q6S3Lh2!AJAkH_$9by<+j_^~U)}Z%hn~T|dO3?z!vdAX)7^M7{znLz89XR6fb*QT&6WaP$Zo>gRg!Q#&ij`j~Fh8PV|Y%Z5| zjm{_7qiD_;p21GpWTep$%hJ)OaWbCy6wO`4PFZAx{@Y?!fY78R?2AxPkb_+d0{xM} zwod@9bFw?i)=)qbj1Q zk>A8g8SW4iiVhs+O)Ol< zTmuKDC2&z>KzvhC#OEQazO0%e4R0^ED*gH+yh=@SrH`*ya~2)rgNj0ixRY%1BJOYA z)f+7`{i$z0!ej6b8QlS%$uLmc^HEFt#1%Xkq-n{h^_7>V8}<+Rah2$S!8^mT#E!H4 zEZ#N4`wicwO5ggH7p=u?>B$re*pN)G0q!Rg-grGeh49Mj>r!aGOr`gv&|<*V6iSBp z=~T*sr8$+#0pF)m0U*v!F9EvjR3XLt9kd1ppMz=vKRAR$Za6KC3Sl{zMs7pH3 zb9XxV0AuOY2H3Hjb^|7_pe=yT6{aREgSI1~o?(Vh&NQRCGiejVGqdPrz@99#)`Kis z1xrb`+0U75Q|!p0dbmEy5t(H_Nx8Hc@Odt^qgFv4RRT`t(KEfcNE+1IQ#mw9J3y#dRn za&zJ0*rW966_kJ$N-M|>__RWtjGQ3eZC37Zle&42R8l@%)s<8Y7^$SyfcYLO2JG~R z7G(Hc4>bcCt7s$Oc9oggQ%z0i<#M%o3$kjc1eU%UvuK3Z)a>$_=Xu{NI+Qb&*P55{ WTrIW0Vy`0?O1xi3O97*GRR1qb*_faJ delta 1876 zcmY+Ddr(wW9LMiDKS0D?lQeR`Or11P6U4GqkOtTVdA}DBgk^z+5_Sv`QYp!iKy9!# zybX`6K^z3-F>775KPsDal&GA+naU_eEz|yJtY%C=rhC8V^hY~$_k8C2d!NVXn0a)} zY)(rgFF!@`^;ItP+{`4Q|U!^OQl>Y~uSdD;d&l-!{#G;JV zlfwGFAWVB(Ay|AqSqh-w$k}s^+Z796l-sWi0<>bRFXy*JD~aeJO;=GJiz@1EO#qq?W_PWJ+Tqj%TC$!NQEMsuC#OE%q3{mdUhUu>Tera7TlsXu`m2U-d1)K`=kAy$eQLr+i7F>g;rh2 zUKHAJ1?x7!YO7^Cd_W_6*uBM|PutlBQEaM<#rZ?C3^Kpvm~=9?Dy2X_rNmi^l4?w< z78TGut!s?!5kW!Uv1@|JfYQkuP%JE6I!z1OKT z^&EFYTktBc7dz2fm#1&#OdUER& zYJNT_vQNK)$GjCK8nwQ1(h|+`5kDgmO>4YUoOs7&ep%cb=QsR-DE-H`JZA@Hi;t#I z!0u>z4KNi=c=2j@4B^SEJ7Z`KOg%BQ5pW}hq5)6GQZk?}mWlx1#}aO{8fKxFpmbTN z*np!}+77{Gr3%0gR_PKKN7+!0#Ze_-PP}aAa6Gx7%*0bOpk)gk1}sdVMnGqR^qHGT zO;FTC8NM({Ms+9AUYM38)60M($+FgTGHr#DpCbFYlp>w2sZ<5i$Eh-NR2uDrav_ac z0a@u(0yvjWI{{%CGz_?vK@EV?OmYJzGpPm8ltudirfli~oXMsdz{(tY9dIf~wi;-Y zTi0Tv-7xv&N}uNcz%P#u!ldR=HNZEY-UPgpPby&bRw@UyZI$~IP#`C5E09q>g%aut zWzk;?Wvkn_$<4gKjov_>y+v|c^kWZFmlP8ovszG0cGy2FmM0T#mz6v0ByQd#C6o!D zvJxr-jF!+gz#0eT0S-E33lAJr2c@Qz_5kjc%FK>3s)cgBOe)Fcln Date: Thu, 8 Aug 2024 19:16:22 +0530 Subject: [PATCH 10/45] fix: tests (#222) --- CHANGELOG.md | 4 ++++ build.gradle | 2 +- .../test/multitenancy/TestForNoCrashDuringStartup.java | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7b13708..5be1e95c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [7.1.1] - 2024-08-08 + +- Fixes tests that check for `Internal Error` in 500 status responses + ## [7.1.0] - Compatible with plugin interface version 6.2 diff --git a/build.gradle b/build.gradle index fdcd09f6..5d172564 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "7.1.0" +version = "7.1.1" repositories { mavenCentral() diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java index 801737fd..5081c414 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java @@ -112,7 +112,7 @@ public void testThatCUDRecoversWhenItFailsToAddEntryDuringCreation() throws Exce fail(); } catch (HttpResponseException e) { // ignore - assertTrue(e.getMessage().contains("Internal Error")); // retried creating tenant entry + assertEquals("Http error. Status Code: 500. Message: java.sql.SQLException: Simulated error in addTenantIdInTargetStorage", e.getMessage()); } MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false; @@ -165,7 +165,7 @@ public void testThatCUDRecoversWhenTheDbIsDownDuringCreationButDbComesUpLater() fail(); } catch (HttpResponseException e) { // ignore - assertTrue(e.getMessage().contains("Internal Error")); // db is still down + assertEquals("Http error. Status Code: 500. Message: java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage()); } update(start, "CREATE DATABASE st5000;", pst -> { @@ -546,7 +546,7 @@ public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throw fail(); } catch (HttpResponseException e) { // ignore - assertTrue(e.getMessage().contains("Internal Error")); // db is still down + assertEquals("Http error. Status Code: 500. Message: java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage()); } process.kill(false); From 60b6e0f3672c662081017abd791fd59a2e55b22d Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 8 Aug 2024 19:23:46 +0530 Subject: [PATCH 11/45] adding dev-v7.1.1 tag to this commit to ensure building --- ...-7.1.0.jar => postgresql-plugin-7.1.1.jar} | Bin 223821 -> 223821 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename jar/{postgresql-plugin-7.1.0.jar => postgresql-plugin-7.1.1.jar} (95%) diff --git a/jar/postgresql-plugin-7.1.0.jar b/jar/postgresql-plugin-7.1.1.jar similarity index 95% rename from jar/postgresql-plugin-7.1.0.jar rename to jar/postgresql-plugin-7.1.1.jar index c538f8a8f357bbfea3f20776779ebfa2a1a8b5d3..541e501b15eb568dc17265bdb70e1e5a97ba19a4 100644 GIT binary patch delta 2184 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 GEd37**#7VU From 0d115f2c7f2c3293792379fb0d7677a16c2f740e Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 2 Sep 2024 15:40:43 +0530 Subject: [PATCH 12/45] fix: slow query fix (#223) * fix: slow query fix * fix: version and changelog --- CHANGELOG.md | 4 ++++ build.gradle | 2 +- .../storage/postgresql/queries/GeneralQueries.java | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5be1e95c..b21ce161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [7.1.2] - 2024-09-02 + +- Optimizes users count query + ## [7.1.1] - 2024-08-08 - Fixes tests that check for `Internal Error` in 500 status responses diff --git a/build.gradle b/build.gradle index 5d172564..8ea34db5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "7.1.1" +version = "7.1.2" repositories { mavenCentral() 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 1608d3df..fca3d223 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -705,8 +705,8 @@ public static void deleteKeyValue_Transaction(Start start, Connection con, Tenan public static long getUsersCount(Start start, AppIdentifier appIdentifier, RECIPE_ID[] includeRecipeIds) throws SQLException, StorageQueryException { StringBuilder QUERY = new StringBuilder( - "SELECT COUNT(DISTINCT primary_or_recipe_user_id) AS total FROM " + - getConfig(start).getUsersTable()); + "SELECT COUNT(*) AS total FROM ("); + QUERY.append("SELECT primary_or_recipe_user_id FROM " + getConfig(start).getUsersTable()); QUERY.append(" WHERE app_id = ?"); if (includeRecipeIds != null && includeRecipeIds.length > 0) { QUERY.append(" AND recipe_id IN ("); @@ -719,6 +719,7 @@ public static long getUsersCount(Start start, AppIdentifier appIdentifier, RECIP } QUERY.append(")"); } + QUERY.append(" GROUP BY primary_or_recipe_user_id) AS uniq_users"); return execute(start, QUERY.toString(), pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -739,7 +740,8 @@ public static long getUsersCount(Start start, AppIdentifier appIdentifier, RECIP public static long getUsersCount(Start start, TenantIdentifier tenantIdentifier, RECIPE_ID[] includeRecipeIds) throws SQLException, StorageQueryException { StringBuilder QUERY = new StringBuilder( - "SELECT COUNT(DISTINCT primary_or_recipe_user_id) AS total FROM " + getConfig(start).getUsersTable()); + "SELECT COUNT(*) AS total FROM ("); + QUERY.append("SELECT primary_or_recipe_user_id FROM " + getConfig(start).getUsersTable()); QUERY.append(" WHERE app_id = ? AND tenant_id = ?"); if (includeRecipeIds != null && includeRecipeIds.length > 0) { QUERY.append(" AND recipe_id IN ("); @@ -753,6 +755,8 @@ public static long getUsersCount(Start start, TenantIdentifier tenantIdentifier, QUERY.append(")"); } + QUERY.append(" GROUP BY primary_or_recipe_user_id) AS uniq_users"); + return execute(start, QUERY.toString(), pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); From b933a5457aa87bf8f805af26021478b2e634a1a2 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 2 Sep 2024 16:03:47 +0530 Subject: [PATCH 13/45] adding dev-v7.1.2 tag to this commit to ensure building --- ...-7.1.1.jar => postgresql-plugin-7.1.2.jar} | Bin 223821 -> 223871 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename jar/{postgresql-plugin-7.1.1.jar => postgresql-plugin-7.1.2.jar} (84%) diff --git a/jar/postgresql-plugin-7.1.1.jar b/jar/postgresql-plugin-7.1.2.jar similarity index 84% rename from jar/postgresql-plugin-7.1.1.jar rename to jar/postgresql-plugin-7.1.2.jar index 541e501b15eb568dc17265bdb70e1e5a97ba19a4..455df7bed67c60a8ab0a6cef9f48c1cfbd64ae52 100644 GIT binary patch delta 26989 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 G1NlFudTFHq delta 26809 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 From c9436ff0b63cc657aacc3371c9411694bb6722a3 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 4 Sep 2024 16:50:30 +0530 Subject: [PATCH 14/45] fix: index for MAU (#224) * fix: index for MAU * fix: feature flags --- CHANGELOG.md | 10 +++ build.gradle | 2 +- .../supertokens/storage/postgresql/Start.java | 9 +++ .../queries/ActiveUsersQueries.java | 23 +++++++ .../postgresql/queries/GeneralQueries.java | 2 + .../postgresql/test/OneMillionUsersTest.java | 69 ++++++++++++++++++- 6 files changed, 112 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b21ce161..30b7400d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [7.1.3] - 2024-09-04 + +- Adds index on `last_active_time` for `user_last_active` table to improve the performance of MAU computation. + +### Migration + +```sql +CREATE INDEX IF NOT EXISTS user_last_active_last_active_time_index ON user_last_active (last_active_time DESC, app_id DESC); +``` + ## [7.1.2] - 2024-09-02 - Optimizes users count query diff --git a/build.gradle b/build.gradle index 8ea34db5..bab06c3e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "7.1.2" +version = "7.1.3" repositories { mavenCentral() diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index a0eea865..aee997d4 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -1332,6 +1332,15 @@ public void updateLastActive(AppIdentifier appIdentifier, String userId) throws } } + @TestOnly + public void updateLastActive(AppIdentifier appIdentifier, String userId, long timestamp) throws StorageQueryException { + try { + ActiveUsersQueries.updateUserLastActive(this, appIdentifier, userId, timestamp); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public int countUsersActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException { try { 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 327ed6ce..ccd589ac 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java @@ -9,6 +9,8 @@ import java.sql.Connection; import java.sql.SQLException; +import org.jetbrains.annotations.TestOnly; + import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; import static io.supertokens.storage.postgresql.config.Config.getConfig; @@ -34,6 +36,11 @@ static String getQueryToCreateAppIdIndexForUserLastActiveTable(Start start) { + Config.getConfig(start).getUserLastActiveTable() + "(app_id);"; } + public static String getQueryToCreateLastActiveTimeIndexForUserLastActiveTable(Start start) { + return "CREATE INDEX IF NOT EXISTS user_last_active_last_active_time_index ON " + + Config.getConfig(start).getUserLastActiveTable() + "(last_active_time DESC, app_id DESC);"; + } + public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) throws SQLException, StorageQueryException { String QUERY = "SELECT COUNT(*) as total FROM " + Config.getConfig(start).getUserLastActiveTable() @@ -90,6 +97,22 @@ public static int updateUserLastActive(Start start, AppIdentifier appIdentifier, }); } + @TestOnly + public static int updateUserLastActive(Start start, AppIdentifier appIdentifier, String userId, long timestamp) + throws SQLException, StorageQueryException { + String QUERY = "INSERT INTO " + Config.getConfig(start).getUserLastActiveTable() + + + "(app_id, user_id, last_active_time) VALUES(?, ?, ?) ON CONFLICT(app_id, user_id) DO UPDATE SET " + + "last_active_time = ?"; + + return update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + pst.setLong(3, timestamp); + pst.setLong(4, timestamp); + }); + } + public static Long getLastActiveByUserId(Start start, AppIdentifier appIdentifier, String userId) throws StorageQueryException { String QUERY = "SELECT last_active_time FROM " + Config.getConfig(start).getUserLastActiveTable() 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 fca3d223..ac922852 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -303,6 +303,8 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S // Index update(con, ActiveUsersQueries.getQueryToCreateAppIdIndexForUserLastActiveTable(start), NO_OP_SETTER); + update(con, ActiveUsersQueries.getQueryToCreateLastActiveTimeIndexForUserLastActiveTable(start), + NO_OP_SETTER); } if (!doesTableExists(start, con, Config.getConfig(start).getAccessTokenSigningKeysTable())) { diff --git a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java index 5f55bcf1..8a506c38 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java @@ -27,8 +27,11 @@ import io.supertokens.emailpassword.EmailPassword; import io.supertokens.emailpassword.ParsedFirebaseSCryptResponse; import io.supertokens.featureflag.EE_FEATURES; +import io.supertokens.featureflag.FeatureFlag; import io.supertokens.featureflag.FeatureFlagTestContent; import io.supertokens.passwordless.Passwordless; +import io.supertokens.pluginInterface.RECIPE_ID; +import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.authRecipe.LoginMethod; import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage; @@ -39,6 +42,7 @@ import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.session.Session; import io.supertokens.session.info.SessionInformationHolder; +import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.test.httpRequest.HttpRequestForTesting; import io.supertokens.storageLayer.StorageLayer; import io.supertokens.thirdparty.ThirdParty; @@ -386,6 +390,30 @@ private void createSessions(Main main) throws Exception { es.awaitTermination(10, TimeUnit.MINUTES); } + private void createActiveUserEntries(Main main) throws Exception { + System.out.println("Creating active user entries..."); + + ExecutorService es = Executors.newFixedThreadPool(NUM_THREADS); + + for (String userId : allPrimaryUserIds) { + String finalUserId = userId; + es.execute(() -> { + try { + Storage storage = StorageLayer.getBaseStorage(main); + Start start = (Start) storage; + + start.updateLastActive(new AppIdentifier(null, null), finalUserId, System.currentTimeMillis() - new Random().nextInt(1000 * 3600 * 24 * 60)); + + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + es.shutdown(); + es.awaitTermination(10, TimeUnit.MINUTES); + } + @Test public void testCreatingOneMillionUsers() throws Exception { if (System.getenv("ONE_MILLION_USERS_TEST") == null) { @@ -400,7 +428,7 @@ public void testCreatingOneMillionUsers() throws Exception { FeatureFlagTestContent.getInstance(process.getProcess()) .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ - EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY, EE_FEATURES.MFA, EE_FEATURES.DASHBOARD_LOGIN}); process.startProcess(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -445,6 +473,13 @@ public void testCreatingOneMillionUsers() throws Exception { System.out.println("Time taken to create sessions: " + ((en - st) / 1000) + " sec"); } + { + long st = System.currentTimeMillis(); + createActiveUserEntries(process.getProcess()); + long en = System.currentTimeMillis(); + System.out.println("Time taken to create active user entries: " + ((en - st) / 1000) + " sec"); + } + sanityCheckAPIs(process.getProcess()); allUserIds.clear(); allPrimaryUserIds.clear(); @@ -466,7 +501,7 @@ public void testCreatingOneMillionUsers() throws Exception { FeatureFlagTestContent.getInstance(process.getProcess()) .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ - EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY, EE_FEATURES.MFA, EE_FEATURES.DASHBOARD_LOGIN}); process.startProcess(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); @@ -888,6 +923,36 @@ private void measureOperations(Main main) throws Exception { return null; }); System.out.println("Update user metadata " + time); + assert time < 3000; + } + + { // measure user counting + long time = measureTime(() -> { + try { + AuthRecipe.getUsersCount(main, null); + AuthRecipe.getUsersCount(main, new RECIPE_ID[]{RECIPE_ID.EMAIL_PASSWORD}); + AuthRecipe.getUsersCount(main, new RECIPE_ID[]{RECIPE_ID.EMAIL_PASSWORD, RECIPE_ID.THIRD_PARTY}); + } catch (Exception e) { + errorCount.incrementAndGet(); + throw new RuntimeException(e); + } + return null; + }); + System.out.println("User counting: " + time); + assert time < 3000; + } + { // measure telemetry + long time = measureTime(() -> { + try { + FeatureFlag.getInstance(main).getPaidFeatureStats(); + } catch (Exception e) { + errorCount.incrementAndGet(); + throw new RuntimeException(e); + } + return null; + }); + System.out.println("Telemetry: " + time); + assert time < 3000; } assertEquals(0, errorCount.get()); From 28bcc7d4b038c337fa5533a11bf2684a36171214 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 4 Sep 2024 16:53:19 +0530 Subject: [PATCH 15/45] adding dev-v7.1.3 tag to this commit to ensure building --- ...-7.1.2.jar => postgresql-plugin-7.1.3.jar} | Bin 223871 -> 224159 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename jar/{postgresql-plugin-7.1.2.jar => postgresql-plugin-7.1.3.jar} (70%) diff --git a/jar/postgresql-plugin-7.1.2.jar b/jar/postgresql-plugin-7.1.3.jar similarity index 70% rename from jar/postgresql-plugin-7.1.2.jar rename to jar/postgresql-plugin-7.1.3.jar index 455df7bed67c60a8ab0a6cef9f48c1cfbd64ae52..312634706318523c7f71cb8886d9c234f0773dbf 100644 GIT binary patch delta 59751 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

2-xUQL>3-3I-aB;E0D#ONFL!0|jQX!4 zw3g{%eljQO5a@&e0N|(pm#)V8UvyL8gTMK#HiJ%g7W5Z}E*UQDN`RSM;AUuh4n0Ga zq2d3#yCdBG>Of~o4tZ)0q=crnArjfTY1A^2v-msH6+gBcUl|n zAT^(RD1{$-I+0rfiD4OsUXf(*o&I+aD+Fx$`=m{>gh~Mc_tFr5cuY1;6*zc&;GetN zC?lJJy$JrnMVV9E;W zRz2xHPAqF67c4|6Yp7HwSRG|K_h*LznQb z0B`S1A?5!uMFEpq+CZfQW%ttC3U~}iz0L21czFle{5OOO0#d=WSV2HCI10!B2wH%C zARr6utibbPeTW9l>oD}A`AUO_>jso9Fr5o<0&<5Tl zuuQDnNl%*Yr4;tR3n9xlzKRLz^2h(URd2hex;a3vkNH-3Qb>dSZ?`@6P*ZE|_xO!H zkPYU5-2p1yef~#_8YWG>lSYrjq*>$8{d45-+sf4)qBC>P7jpy(z{=>K7Uch|!}9Ds z^{yp3q{s31rE*94&sB=|hktjKCdxp*Is&O-t2>y6{E``pLT?^`;2+lA@z5s&;dBBj LBZlW~Tn5Yqg delta 59605 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 Oxj|Gh>E(gF4f=mRl^1CM From 1a30acd9ef507ec8ae0399332a5796f372dec6a8 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 4 Sep 2024 23:09:19 +0530 Subject: [PATCH 16/45] fix: Test fix (#225) * fix: for cicd * fix: test --- .../storage/postgresql/test/OneMillionUsersTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java index 8a506c38..ea29a8f9 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java @@ -939,7 +939,7 @@ private void measureOperations(Main main) throws Exception { return null; }); System.out.println("User counting: " + time); - assert time < 3000; + assert time < 10000; } { // measure telemetry long time = measureTime(() -> { @@ -952,7 +952,7 @@ private void measureOperations(Main main) throws Exception { return null; }); System.out.println("Telemetry: " + time); - assert time < 3000; + assert time < 6000; } assertEquals(0, errorCount.get()); From e40602c40354a5e4765769b2d62d1e19795e8b41 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 4 Sep 2024 23:09:41 +0530 Subject: [PATCH 17/45] adding dev-v7.1.3 tag to this commit to ensure building --- jar/postgresql-plugin-7.1.3.jar | Bin 224159 -> 224159 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.1.3.jar b/jar/postgresql-plugin-7.1.3.jar index 312634706318523c7f71cb8886d9c234f0773dbf..af33d925b81e1fff1ef43e143847d48be8baf1d2 100644 GIT binary patch delta 1557 zcmYL|drZ}37{|~1J|cI+sOAPa*>q9Tz>pDx(Buy1a1jvB;d0<`IUI)6zl!IZ(Cy92d{1j4hGln~gg0<9LW4iNCEeoOJ)oBgan-yvW7# zhbXbh;mdXkXT-qHtr2vrc%;BhT>D7~$V213g=%EiHeZBCvZB8605`>&7@W{?xZ=Ee+nI;M zIrGGbWpp_dn@VTCC**!<_Dgxl%KRe7`onum47_u{|0(d~sxw$n3kzbIg6_5;X60g| z`_8j05NX}ZA*^m43~K~S!&&Z2k*rA?o1@vs&}g%{YpP*2en%5Xx?0&1+#yw6Q-n!WjF2#i(A-4Z zS%7il8eefS-!0Im-(<2)ci=Rel%@T4n^~lh^$Q!oB-~4L?7}k;4=%G*+1cVM zJ1jL#oZomFq4*8lDMLR8^Q$s6w28Z9N5-wZGX|QON`BEBhP{D@VW#fSn)rDsuHVlW zFkQE?h1VddxDng6od>z6w%W@5P=((?-YSjmW85I?nmo=sWlmEMCyZV*+j%myLw0VH z+9&(Czb7Ig{alYoV?Q6m6>>j6$gR?NXNb2*!{-dQNTcB_uad>w9OiGxIx{bD7mOU+~p_cal7%hVjj`wP~Z%P9s)&zF-0TyGJ>7VNU9WDhLV48N>`^1v$Jf35 ZO2-U!EJTa@GpVDEGz&3 delta 1557 zcmYL|X-rgC7>4JZ4`ioU+QtNGB+-;s8nBj70!1s^05dEO2s11*49hT37SU2s&;kZ( zP084)Tunpg538sd1s#$u=?`j+wds-y!Ae^KR;AitYe)kv>3Prn(Mje$-|~LTJ@;PM zOpt3PC@?pRy!jPRJSE+k-nBV zYCw3umuj7WA}*F&l)Q zyU(*wtm9dYV0FPTY%wf@M2+Y2PS%8`o~?206KItA1Xh5+Yl&>iAI5qbTl9vpsAD|= zFzSog-!ft=V{gdFIXfE&L@Kw1HE%%UsW$fKW<(}GV(}C_D(>~X;du1J5RGOw!TI<> zjADsnA)Z|>_D=|8ue({mHWX&wV&XQ15jxAJ*Ze4CwQlvY3k*KqgTuho`x>N z4sMs)r~7%34>A!0T!&2K0H2hDUL4{!X?!ruTcqJX%B|99ILE6P*1I*v-BaQ07Khmfiy1aC%hyO)7 z*}?5vkr(kZTG|ILYpE9Tk_>hJTn074&&i|{$luPS_u<>Ks1tmYrRL2#>Oua#POY=+ zRs9n^wIhBYo8E+eK3nt`zCMRaz_A>4{-#`2r{q!t;y!tzpU^w{Oh9yn7#$HCHlG=zTMg>(@9kwSV0{I^i-C0JLap3Chb zIs#pxQO(bS!Y?+_N$BpIs1d%gnBD~!is=m4UP3nTw-Ryxg0@m|Kk!RcePNl(>t*Vk zIJ4MGQj4tr>&X84v0$_KAj zP&K%@lFZ<-N-}{zRf?g4nN@TMoT{QWa8I?`Z=#x7utbzieV8BHs2Kj5O`TI&qvr3_ jh=(KMI=k3M&}~;=qOe+Oh2K|8M)ZALOG)64I%@b2j3QaO From 4404916f1a689c7644467be3b29921cff200f66f Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 20 Sep 2024 16:56:41 +0200 Subject: [PATCH 18/45] fix: removing restriction of connection pool size for bulk import --- CHANGELOG.md | 2 ++ .../storage/postgresql/BulkImportProxyStorage.java | 11 +---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 247a91ec..abe9ede1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Unrestricts the connection pool size for the Bulk Import + ## [7.1.0] - 2024-04-25 - Adds queries for Bulk Import diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java index f4e8dd8e..0f933747 100644 --- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java +++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java @@ -48,7 +48,7 @@ public synchronized Connection getTransactionConnection() throws SQLException, S if (this.connection == null) { Connection con = ConnectionPool.getConnectionForProxyStorage(this); this.connection = new BulkImportProxyConnection(con); - connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); connection.setAutoCommit(false); } return this.connection; @@ -66,15 +66,6 @@ public void commitTransaction(TransactionConnection con) throws StorageQueryExce // if any query fails while importing the user } - @Override - public void loadConfig(JsonObject configJson, Set logLevels, TenantIdentifier tenantIdentifier) - throws InvalidConfigException { - // We are overriding the loadConfig method to set the connection pool size - // to 1 to avoid creating many connections for the bulk import cronjob - configJson.addProperty("postgresql_connection_pool_size", 1); - super.loadConfig(configJson, logLevels, tenantIdentifier); - } - @Override public void initStorage(boolean shouldWait, List tenantIdentifiers) throws DbInitException { super.initStorage(shouldWait, tenantIdentifiers); From 9c093fb1836d2ee1595b0811fa9706a16435635f Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 27 Sep 2024 14:14:29 +0200 Subject: [PATCH 19/45] fix: actually closing the connection --- .../supertokens/storage/postgresql/BulkImportProxyStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java index 0f933747..59f834a7 100644 --- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java +++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java @@ -86,7 +86,7 @@ public void initStorage(boolean shouldWait, List tenantIdentif public void closeConnectionForBulkImportProxyStorage() throws StorageQueryException { try { if (this.connection != null) { - this.connection.close(); + this.connection.closeForBulkImportProxyStorage(); this.connection = null; } ConnectionPool.close(this); From e115ebe6971b4125fc6096ea8adbb58351c1de27 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Mon, 30 Sep 2024 12:18:20 +0200 Subject: [PATCH 20/45] fix: add bulk import retry logic for postgres too --- .../java/io/supertokens/storage/postgresql/Start.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 411e52b3..6d562937 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -26,6 +26,7 @@ import io.supertokens.pluginInterface.authRecipe.LoginMethod; import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage; import io.supertokens.pluginInterface.bulkimport.BulkImportStorage.BULK_IMPORT_USER_STATUS; +import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportTransactionRolledBackException; import io.supertokens.pluginInterface.bulkimport.sqlStorage.BulkImportSQLStorage; import io.supertokens.pluginInterface.bulkimport.BulkImportUser; import io.supertokens.pluginInterface.dashboard.DashboardSearchTags; @@ -322,6 +323,13 @@ public T startTransaction(TransactionLogic logic, TransactionIsolationLev if ((isPSQLRollbackException || isDeadlockException) && tries < NUM_TRIES) { try { + if(this instanceof BulkImportProxyStorage){ + throw new StorageTransactionLogicException(new BulkImportTransactionRolledBackException(e)); + // if the current instance is of BulkImportProxyStorage, that means we are doing a bulk import + // which uses nested transactions. With MySQL this retry logic doesn't going to work, we have + // to retry the whole "big" transaction, not just the innermost, current one. + // @see BulkImportTransactionRolledBackException for more explanation. + } Thread.sleep((long) (10 + (250 + Math.min(Math.pow(2, tries), 3000)) * Math.random())); } catch (InterruptedException ignored) { } From a6ed05f0e01b92c62cc73e2ea44368bd0b3537d3 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Wed, 2 Oct 2024 10:58:22 +0200 Subject: [PATCH 21/45] fix: fix failing tests --- src/main/java/io/supertokens/storage/postgresql/Start.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 0e27980d..206f58a4 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -25,6 +25,7 @@ import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.authRecipe.LoginMethod; import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage; +import io.supertokens.pluginInterface.bulkimport.BulkImportStorage; import io.supertokens.pluginInterface.bulkimport.BulkImportStorage.BULK_IMPORT_USER_STATUS; import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportTransactionRolledBackException; import io.supertokens.pluginInterface.bulkimport.sqlStorage.BulkImportSQLStorage; @@ -898,6 +899,8 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi } } else if (className.equals(JWTRecipeStorage.class.getName())) { /* Since JWT recipe tables do not store userId we do not add any data to them */ + } else if (className.equals(BulkImportStorage.class.getName())){ + //ignore } else if (className.equals(ActiveUsersStorage.class.getName())) { try { ActiveUsersQueries.updateUserLastActive(this, tenantIdentifier.toAppIdentifier(), userId); From f474b2cb65bfcb972391bcf6f1780c30f7e7b6d2 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 4 Oct 2024 10:58:27 +0530 Subject: [PATCH 22/45] feat: OAuth provider support (#228) * fix: oauth clients table * fix: oauth db changes * fix: listClientsForApp * fix: revoke (#226) * fix: revoke * fix: pr comment * fix: interface * fix: update * fix: oauth stats queries * fix: revoke and cleanup * fix: stats * fix: logout queries (#229) * fix: update queries * fix: versions * revert * fix: changelog * fix: changelog * fix: constraints --- CHANGELOG.md | 5 + build.gradle | 2 +- pluginInterfaceSupported.json | 2 +- .../supertokens/storage/postgresql/Start.java | 198 ++++++++- .../postgresql/config/PostgreSQLConfig.java | 16 + .../postgresql/queries/GeneralQueries.java | 35 ++ .../postgresql/queries/OAuthQueries.java | 388 ++++++++++++++++++ 7 files changed, 642 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 30b7400d..1dd709e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [7.2.0] - 2024-10-03 + +- Compatible with plugin interface version 6.3 +- Adds support for OAuthStorage + ## [7.1.3] - 2024-09-04 - Adds index on `last_active_time` for `user_last_active` table to improve the performance of MAU computation. diff --git a/build.gradle b/build.gradle index bab06c3e..553a2c88 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "7.1.3" +version = "7.2.0" repositories { mavenCentral() diff --git a/pluginInterfaceSupported.json b/pluginInterfaceSupported.json index 0dedee88..25f82381 100644 --- a/pluginInterfaceSupported.json +++ b/pluginInterfaceSupported.json @@ -1,6 +1,6 @@ { "_comment": "contains a list of plugin interfaces branch names that this core supports", "versions": [ - "6.2" + "6.3" ] } \ No newline at end of file diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index aee997d4..33cd8c17 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -54,6 +54,11 @@ import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.multitenancy.sqlStorage.MultitenancySQLStorage; +import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge; +import io.supertokens.pluginInterface.oauth.OAuthRevokeTargetType; +import io.supertokens.pluginInterface.oauth.OAuthStorage; +import io.supertokens.pluginInterface.oauth.exception.DuplicateOAuthLogoutChallengeException; +import io.supertokens.pluginInterface.oauth.exception.OAuthClientNotFoundException; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; import io.supertokens.pluginInterface.passwordless.exception.*; @@ -106,7 +111,7 @@ public class Start implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage, JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage, UserIdMappingSQLStorage, MultitenancyStorage, MultitenancySQLStorage, DashboardSQLStorage, TOTPSQLStorage, - ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage { + ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage, OAuthStorage { // these configs are protected from being modified / viewed by the dev using the SuperTokens // SaaS. If the core is not running in SuperTokens SaaS, this array has no effect. @@ -121,7 +126,6 @@ public class Start private ResourceDistributor resourceDistributor = new ResourceDistributor(); private String processId; private HikariLoggingAppender appender; - private static final String APP_ID_KEY_NAME = "app_id"; private static final String ACCESS_TOKEN_SIGNING_KEY_NAME = "access_token_signing_key"; private static final String REFRESH_TOKEN_KEY_NAME = "refresh_token_key"; public static boolean isTesting = false; @@ -864,6 +868,8 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi } } else if (className.equals(JWTRecipeStorage.class.getName())) { /* Since JWT recipe tables do not store userId we do not add any data to them */ + } else if (className.equals(OAuthStorage.class.getName())) { + /* Since OAuth recipe tables do not store userId we do not add any data to them */ } else if (className.equals(ActiveUsersStorage.class.getName())) { try { ActiveUsersQueries.updateUserLastActive(this, tenantIdentifier.toAppIdentifier(), userId); @@ -3089,6 +3095,194 @@ public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(A } } + @Override + public boolean doesOAuthClientIdExist(AppIdentifier appIdentifier, String clientId) + throws StorageQueryException { + try { + return OAuthQueries.doesOAuthClientIdExist(this, clientId, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void addOrUpdateOauthClient(AppIdentifier appIdentifier, String clientId, boolean isClientCredentialsOnly) + throws StorageQueryException, TenantOrAppNotFoundException { + try { + OAuthQueries.addOrUpdateOauthClient(this, appIdentifier, clientId, isClientCredentialsOnly); + } catch (SQLException e) { + PostgreSQLConfig config = Config.getConfig(this); + if (e instanceof PSQLException) { + ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); + + if (isForeignKeyConstraintError(serverMessage, config.getOAuthClientsTable(), "app_id")) { + throw new TenantOrAppNotFoundException(appIdentifier); + } + } + throw new StorageQueryException(e); + } + } + + @Override + public boolean deleteOAuthClient(AppIdentifier appIdentifier, String clientId) throws StorageQueryException { + try { + return OAuthQueries.deleteOAuthClient(this, clientId, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public List listOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException { + try { + return OAuthQueries.listOAuthClients(this, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void revokeOAuthTokensBasedOnTargetFields(AppIdentifier appIdentifier, OAuthRevokeTargetType targetType, String targetValue, long exp) + throws StorageQueryException, TenantOrAppNotFoundException { + try { + OAuthQueries.revokeOAuthTokensBasedOnTargetFields(this, appIdentifier, targetType, targetValue, exp); + } catch (SQLException e) { + PostgreSQLConfig config = Config.getConfig(this); + if (e instanceof PSQLException) { + ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); + + if (isForeignKeyConstraintError(serverMessage, config.getOAuthRevokeTable(), "app_id")) { + throw new TenantOrAppNotFoundException(appIdentifier); + } + } + throw new StorageQueryException(e); + } + + } + + @Override + public boolean isOAuthTokenRevokedBasedOnTargetFields(AppIdentifier appIdentifier, OAuthRevokeTargetType[] targetTypes, String[] targetValues, long issuedAt) + throws StorageQueryException { + try { + return OAuthQueries.isOAuthTokenRevokedBasedOnTargetFields(this, appIdentifier, targetTypes, targetValues, issuedAt); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void addOAuthM2MTokenForStats(AppIdentifier appIdentifier, String clientId, long iat, long exp) + throws StorageQueryException, OAuthClientNotFoundException { + try { + OAuthQueries.addOAuthM2MTokenForStats(this, appIdentifier, clientId, iat, exp); + } catch (SQLException e) { + PostgreSQLConfig config = Config.getConfig(this); + if (e instanceof PSQLException) { + ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); + + if (isForeignKeyConstraintError(serverMessage, config.getOAuthM2MTokensTable(), "client_id")) { + throw new OAuthClientNotFoundException(); + } + } + throw new StorageQueryException(e); + } + } + + @Override + public void cleanUpExpiredAndRevokedOAuthTokensList() throws StorageQueryException { + try { + OAuthQueries.cleanUpExpiredAndRevokedOAuthTokensList(this); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void addOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge, String clientId, + String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) + throws StorageQueryException, DuplicateOAuthLogoutChallengeException, OAuthClientNotFoundException { + try { + OAuthQueries.addOAuthLogoutChallenge(this, appIdentifier, challenge, clientId, postLogoutRedirectionUri, sessionHandle, state, timeCreated); + } catch (SQLException e) { + PostgreSQLConfig config = Config.getConfig(this); + if (e instanceof PSQLException) { + ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); + + if (isPrimaryKeyError(serverMessage, config.getOAuthLogoutChallengesTable())) { + throw new DuplicateOAuthLogoutChallengeException(); + } else if (isForeignKeyConstraintError(serverMessage, config.getOAuthLogoutChallengesTable(), "client_id")) { + throw new OAuthClientNotFoundException(); + } + } + throw new StorageQueryException(e); + } + } + + @Override + public OAuthLogoutChallenge getOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException { + try { + return OAuthQueries.getOAuthLogoutChallenge(this, appIdentifier, challenge); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void deleteOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException { + try { + OAuthQueries.deleteOAuthLogoutChallenge(this, appIdentifier, challenge); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void deleteOAuthLogoutChallengesBefore(long time) throws StorageQueryException { + try { + OAuthQueries.deleteOAuthLogoutChallengesBefore(this, time); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, false); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfClientCredentialsOnlyOAuthClients(AppIdentifier appIdentifier) + throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, true); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfOAuthM2MTokensCreatedSince(AppIdentifier appIdentifier, long since) + throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfOAuthM2MTokensCreatedSince(this, appIdentifier, since); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfOAuthM2MTokensAlive(AppIdentifier appIdentifier) throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfOAuthM2MTokensAlive(this, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @TestOnly public int getDbActivityCount(String dbname) throws SQLException, StorageQueryException { String QUERY = "SELECT COUNT(*) as c FROM pg_stat_activity WHERE datname = ?;"; 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 a1ff555c..29150141 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -439,6 +439,22 @@ public String getDashboardSessionsTable() { return addSchemaAndPrefixToTableName("dashboard_user_sessions"); } + public String getOAuthClientsTable() { + return addSchemaAndPrefixToTableName("oauth_clients"); + } + + public String getOAuthRevokeTable() { + return addSchemaAndPrefixToTableName("oauth_revoke"); + } + + public String getOAuthM2MTokensTable() { + return addSchemaAndPrefixToTableName("oauth_m2m_tokens"); + } + + public String getOAuthLogoutChallengesTable() { + return addSchemaAndPrefixToTableName("oauth_logout_challenges"); + } + public String getTotpUsersTable() { return addSchemaAndPrefixToTableName("totp_users"); } 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 ac922852..a53f0bdd 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -554,6 +554,37 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S update(con, TOTPQueries.getQueryToCreateTenantIdIndexForUsedCodesTable(start), NO_OP_SETTER); } + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthClientsTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(start, OAuthQueries.getQueryToCreateOAuthClientTable(start), NO_OP_SETTER); + } + + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthRevokeTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(start, OAuthQueries.getQueryToCreateOAuthRevokeTable(start), NO_OP_SETTER); + + // index + update(con, OAuthQueries.getQueryToCreateOAuthRevokeTimestampIndex(start), NO_OP_SETTER); + update(con, OAuthQueries.getQueryToCreateOAuthRevokeExpIndex(start), NO_OP_SETTER); + } + + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthM2MTokensTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(start, OAuthQueries.getQueryToCreateOAuthM2MTokensTable(start), NO_OP_SETTER); + + // index + update(con, OAuthQueries.getQueryToCreateOAuthM2MTokenIatIndex(start), NO_OP_SETTER); + update(con, OAuthQueries.getQueryToCreateOAuthM2MTokenExpIndex(start), NO_OP_SETTER); + } + + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthLogoutChallengesTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(con, OAuthQueries.getQueryToCreateOAuthLogoutChallengesTable(start), NO_OP_SETTER); + + // index + update(con, OAuthQueries.getQueryToCreateOAuthLogoutChallengesTimeCreatedIndex(start), NO_OP_SETTER); + } + } catch (Exception e) { if (e.getMessage().contains("schema") && e.getMessage().contains("does not exist") && numberOfRetries < 1) { @@ -624,6 +655,10 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer + getConfig(start).getUserRolesTable() + "," + getConfig(start).getDashboardUsersTable() + "," + getConfig(start).getDashboardSessionsTable() + "," + + getConfig(start).getOAuthClientsTable() + "," + + getConfig(start).getOAuthRevokeTable() + "," + + getConfig(start).getOAuthM2MTokensTable() + "," + + getConfig(start).getOAuthLogoutChallengesTable() + "," + getConfig(start).getTotpUsedCodesTable() + "," + getConfig(start).getTotpUserDevicesTable() + "," + getConfig(start).getTotpUsersTable(); diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java new file mode 100644 index 00000000..aa2168c9 --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java @@ -0,0 +1,388 @@ +/* + * 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.queries; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge; +import io.supertokens.pluginInterface.oauth.OAuthRevokeTargetType; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import io.supertokens.storage.postgresql.utils.Utils; + +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; + +public class OAuthQueries { + public static String getQueryToCreateOAuthClientTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuthClientsTable = Config.getConfig(start).getOAuthClientsTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuthClientsTable + " (" + + "app_id VARCHAR(64) DEFAULT 'public'," + + "client_id VARCHAR(128) NOT NULL," + + "is_client_credentials_only BOOLEAN NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthClientsTable, null, "pkey") + + " PRIMARY KEY (app_id, client_id)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthClientsTable, "app_id", "fkey") + + " FOREIGN KEY(app_id)" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + "(app_id) ON DELETE CASCADE" + + ");"; + // @formatter:on + } + + public static String getQueryToCreateOAuthRevokeTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuthRevokeTable = Config.getConfig(start).getOAuthRevokeTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuthRevokeTable + " (" + + "app_id VARCHAR(64) DEFAULT 'public'," + + "target_type VARCHAR(16) NOT NULL," + + "target_value VARCHAR(128) NOT NULL," + + "timestamp BIGINT NOT NULL," + + "exp BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthRevokeTable, null, "pkey") + + " PRIMARY KEY (app_id, target_type, target_value)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthRevokeTable, "app_id", "fkey") + + " FOREIGN KEY(app_id)" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + "(app_id) ON DELETE CASCADE" + + ");"; + // @formatter:on + } + + public static String getQueryToCreateOAuthRevokeTimestampIndex(Start start) { + String oAuthRevokeTable = Config.getConfig(start).getOAuthRevokeTable(); + return "CREATE INDEX IF NOT EXISTS oauth_revoke_timestamp_index ON " + + oAuthRevokeTable + "(timestamp DESC, app_id DESC);"; + } + + public static String getQueryToCreateOAuthRevokeExpIndex(Start start) { + String oAuthRevokeTable = Config.getConfig(start).getOAuthRevokeTable(); + return "CREATE INDEX IF NOT EXISTS oauth_revoke_exp_index ON " + + oAuthRevokeTable + "(exp DESC);"; + } + + public static String getQueryToCreateOAuthM2MTokensTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuthM2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuthM2MTokensTable + " (" + + "app_id VARCHAR(64) DEFAULT 'public'," + + "client_id VARCHAR(128) NOT NULL," + + "iat BIGINT NOT NULL," + + "exp BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthM2MTokensTable, null, "pkey") + + " PRIMARY KEY (app_id, client_id, iat)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthM2MTokensTable, "client_id", "fkey") + + " FOREIGN KEY(app_id, client_id)" + + " REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE" + + ");"; + // @formatter:on + } + + public static String getQueryToCreateOAuthM2MTokenIatIndex(Start start) { + String oAuthM2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable(); + return "CREATE INDEX IF NOT EXISTS oauth_m2m_token_iat_index ON " + + oAuthM2MTokensTable + "(iat DESC, app_id DESC);"; + } + + public static String getQueryToCreateOAuthM2MTokenExpIndex(Start start) { + String oAuthM2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable(); + return "CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON " + + oAuthM2MTokensTable + "(exp DESC);"; + } + + public static String getQueryToCreateOAuthLogoutChallengesTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuth2LogoutChallengesTable = Config.getConfig(start).getOAuthLogoutChallengesTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuth2LogoutChallengesTable + " (" + + "app_id VARCHAR(64) DEFAULT 'public'," + + "challenge VARCHAR(128) NOT NULL," + + "client_id VARCHAR(128) NOT NULL," + + "post_logout_redirect_uri VARCHAR(1024)," + + "session_handle VARCHAR(128)," + + "state VARCHAR(128)," + + "time_created BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2LogoutChallengesTable, null, "pkey") + + " PRIMARY KEY (app_id, challenge)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2LogoutChallengesTable, "client_id", "fkey") + + " FOREIGN KEY(app_id, client_id)" + + " REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE" + + ");"; + // @formatter:on + } + + public static String getQueryToCreateOAuthLogoutChallengesTimeCreatedIndex(Start start) { + String oAuth2LogoutChallengesTable = Config.getConfig(start).getOAuthLogoutChallengesTable(); + return "CREATE INDEX IF NOT EXISTS oauth_logout_challenges_time_created_index ON " + + oAuth2LogoutChallengesTable + "(time_created ASC, app_id ASC);"; + } + + public static boolean doesOAuthClientIdExist(Start start, String clientId, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String QUERY = "SELECT app_id FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE client_id = ? AND app_id = ?"; + + return execute(start, QUERY, pst -> { + pst.setString(1, clientId); + pst.setString(2, appIdentifier.getAppId()); + }, ResultSet::next); + } + + public static List listOAuthClients(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String QUERY = "SELECT client_id FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + }, (result) -> { + List res = new ArrayList<>(); + while (result.next()) { + res.add(result.getString("client_id")); + } + return res; + }); + } + + public static void addOrUpdateOauthClient(Start start, AppIdentifier appIdentifier, String clientId, + boolean isClientCredentialsOnly) + throws SQLException, StorageQueryException { + String INSERT = "INSERT INTO " + Config.getConfig(start).getOAuthClientsTable() + + "(app_id, client_id, is_client_credentials_only) VALUES(?, ?, ?) " + + "ON CONFLICT (app_id, client_id) DO UPDATE SET is_client_credentials_only = ?"; + update(start, INSERT, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, clientId); + pst.setBoolean(3, isClientCredentialsOnly); + pst.setBoolean(4, isClientCredentialsOnly); + }); + } + + public static boolean deleteOAuthClient(Start start, String clientId, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ? AND client_id = ?"; + int numberOfRow = update(start, DELETE, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, clientId); + }); + return numberOfRow > 0; + } + + public static void revokeOAuthTokensBasedOnTargetFields(Start start, AppIdentifier appIdentifier, OAuthRevokeTargetType targetType, String targetValue, long exp) + throws SQLException, StorageQueryException { + String INSERT = "INSERT INTO " + Config.getConfig(start).getOAuthRevokeTable() + + "(app_id, target_type, target_value, timestamp, exp) VALUES (?, ?, ?, ?, ?) " + + "ON CONFLICT (app_id, target_type, target_value) DO UPDATE SET timestamp = ?, exp = ?"; + + long currentTime = System.currentTimeMillis() / 1000; + update(start, INSERT, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, targetType.getValue()); + pst.setString(3, targetValue); + pst.setLong(4, currentTime); + pst.setLong(5, exp); + pst.setLong(6, currentTime); + pst.setLong(7, exp); + }); + } + + public static boolean isOAuthTokenRevokedBasedOnTargetFields(Start start, AppIdentifier appIdentifier, OAuthRevokeTargetType[] targetTypes, + String[] targetValues, long issuedAt) + throws SQLException, StorageQueryException { + String QUERY = "SELECT app_id FROM " + Config.getConfig(start).getOAuthRevokeTable() + + " WHERE app_id = ? AND timestamp >= ? AND ("; + + for (int i = 0; i < targetTypes.length; i++) { + QUERY += "(target_type = ? AND target_value = ?)"; + + if (i < targetTypes.length - 1) { + QUERY += " OR "; + } + } + + QUERY += ")"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setLong(2, issuedAt); + + int index = 3; + for (int i = 0; i < targetTypes.length; i++) { + pst.setString(index, targetTypes[i].getValue()); + index++; + pst.setString(index, targetValues[i]); + index++; + } + }, ResultSet::next); + } + + public static int countTotalNumberOfClients(Start start, AppIdentifier appIdentifier, + boolean filterByClientCredentialsOnly) throws SQLException, StorageQueryException { + if (filterByClientCredentialsOnly) { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ? AND is_client_credentials_only = ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setBoolean(2, true); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } else { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } + } + + public static int countTotalNumberOfOAuthM2MTokensAlive(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthM2MTokensTable() + + " WHERE app_id = ? AND exp > ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setLong(2, System.currentTimeMillis()/1000); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } + + public static int countTotalNumberOfOAuthM2MTokensCreatedSince(Start start, AppIdentifier appIdentifier, long since) + throws SQLException, StorageQueryException { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthM2MTokensTable() + + " WHERE app_id = ? AND iat >= ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setLong(2, since / 1000); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } + + public static void addOAuthM2MTokenForStats(Start start, AppIdentifier appIdentifier, String clientId, long iat, long exp) + throws SQLException, StorageQueryException { + String QUERY = "INSERT INTO " + Config.getConfig(start).getOAuthM2MTokensTable() + + " (app_id, client_id, iat, exp) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING"; + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, clientId); + pst.setLong(3, iat); + pst.setLong(4, exp); + }); + } + + public static void cleanUpExpiredAndRevokedOAuthTokensList(Start start) throws SQLException, StorageQueryException { + { + // delete expired M2M tokens + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthM2MTokensTable() + + " WHERE exp < ?"; + + long timestamp = System.currentTimeMillis() / 1000 - 3600 * 24 * 31; // expired 31 days ago + update(start, QUERY, pst -> { + pst.setLong(1, timestamp); + }); + } + + { + // delete expired revoked tokens + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthRevokeTable() + + " WHERE exp < ?"; + + long timestamp = System.currentTimeMillis() / 1000 - 3600 * 24 * 31; // expired 31 days ago + update(start, QUERY, pst -> { + pst.setLong(1, timestamp); + }); + } + } + + public static void addOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge, String clientId, + String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) throws SQLException, StorageQueryException { + String QUERY = "INSERT INTO " + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " (app_id, challenge, client_id, post_logout_redirect_uri, session_handle, state, time_created) VALUES (?, ?, ?, ?, ?, ?, ?)"; + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, challenge); + pst.setString(3, clientId); + pst.setString(4, postLogoutRedirectionUri); + pst.setString(5, sessionHandle); + pst.setString(6, state); + pst.setLong(7, timeCreated); + }); + } + + public static OAuthLogoutChallenge getOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge) throws SQLException, StorageQueryException { + String QUERY = "SELECT challenge, client_id, post_logout_redirect_uri, session_handle, state, time_created FROM " + + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " WHERE app_id = ? AND challenge = ?"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, challenge); + }, result -> { + if (result.next()) { + return new OAuthLogoutChallenge( + result.getString("challenge"), + result.getString("client_id"), + result.getString("post_logout_redirect_uri"), + result.getString("session_handle"), + result.getString("state"), + result.getLong("time_created") + ); + } + return null; + }); + } + + public static void deleteOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge) throws SQLException, StorageQueryException { + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " WHERE app_id = ? AND challenge = ?"; + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, challenge); + }); + } + + public static void deleteOAuthLogoutChallengesBefore(Start start, long time) throws SQLException, StorageQueryException { + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " WHERE time_created < ?"; + update(start, QUERY, pst -> { + pst.setLong(1, time); + }); + } +} From e2b7082dbf18eb1eee5ab5efe125fa4fb0c0b887 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 4 Oct 2024 11:06:25 +0530 Subject: [PATCH 23/45] adding dev-v7.2.0 tag to this commit to ensure building --- ...-7.1.3.jar => postgresql-plugin-7.2.0.jar} | Bin 224159 -> 232868 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename jar/{postgresql-plugin-7.1.3.jar => postgresql-plugin-7.2.0.jar} (65%) diff --git a/jar/postgresql-plugin-7.1.3.jar b/jar/postgresql-plugin-7.2.0.jar similarity index 65% rename from jar/postgresql-plugin-7.1.3.jar rename to jar/postgresql-plugin-7.2.0.jar index af33d925b81e1fff1ef43e143847d48be8baf1d2..4a35f089cc4c30bec0159c89cc2965f994b84504 100644 GIT binary patch delta 74493 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Ækg*Pn@w;{ksr?$HMx zW)FWJpri4FTl|^5@W@g2FaUV~x&X)$C+1m)*|Ep3@UWg!57M@o469%bJ7PQ4B6M}Y ze+;AfPhc|t8O-57hiZNtR`I7`4gV$7@+V;vwtta7leht?6(`A2bfTkSp*TsVE1K;I z2fUq|Sh7Sn1~NYf#mi$1-EWl_4lnhxBcFdj?nfA)CI!KGrxT>GEL#wvW9$)hiI3Ix z5yvK@de+#qt^Dv*kfbw_=>iCPWp8o;1V0#mPB_Wgb37M#HZbBxGI{8q3U3`C+ zEt4sbc?x$(CY>I$M}%zX&TmeNbda_?A(IPHCO$;o(63B7lQMZq$fQZALsga|k|7C+ z^x%%5i=7jY)dT1Fv+~)0qF37wdBuM3QT7albV6j}_1VO09|>MLCZv5sLfTiO@pGg-2eSK0`y^CaNBxWO z-_Pj3jJenUG32_$@8K7VXz)woo;DYmU4s8!x|O}izbry&AHNJMz9ybmi{~-d#q%5D zxmP@I5YKOl=RSV32x2*0!}s%BvaHO>VC%UDFSAv;+C1I$=NlYKl#5zJUbS$L*m z-b|kPxpfZ!Ofmi$3lx|KxCsIb0tss>)>VwGsi<2uvUtUR8h>Ro0ppPBy6Wa>1Xya? zqC|x9nh*oA1bWq0to7&C*Hu3n_3$Djm`CI{dG+vo0{tzD^~eOHqul3h|H7}~DY4Ml(o16WO1k9r1^5XgBrFms#c_jp96dV}BDTzQJ(S+{MgMi1s zzPYhN%42G3S`>&&{7o%2&4@@L%(H;&8>hrUPw3@=-X{2<4}l3CV_MMSZ`_c#-e1{* z_saaW4K+xrB1m4sv+Y{ivNI~74z*TYUpFOE-Uljwj{{)aseQ|^rg)$qfh3#q(gg*1 z>nr^Yl6DUaATT{_w1%3NRn>L*bx6s|ic0@Tf1r00@}~&VlG2xBAsGgFV6X{8Aca8p zAOUt40(HsWI$$!yI9?^Yh`_1b47I> zxYJ1O>#LiZV__7G_CU4?V_+m+}J!N8hh3w&9|2WFa( z3$qAhbv$}Yb9GJA$c6GqpqnMHCbZJ(y5^#aS~P-XF0pI>uDBZ0Y}8V-zYgPMd3BW+ z<{~326(i$eo(YFaX5*zCRF?p|g#>&Jjn%akjT_4A8_OI0mDLUY@)i^bB9WlQCd`+A zph*at)6kG#RaP&>g})X8jD{ZFJrM(bnT+5I3EXthc>M)smIw^IiNNJS$b%sd9(nM} zLySDcf>+9++=S&)4yGjsOMZzE2P-7Ul>{b6#j&gA&^C;Uh6W`<6IMxtvl5|;HSih} z*2;ksaNtAz8&veugLj2hi8#R_kqJ-c_jGBt?mm`etbHz&aC-hV>|* zj$H_2SYx%nY2+M#oxic7W`Wg2yOq&QF`Xx)8D1%kJ_bcOg{3Wxg=64Y4;*L0MmV0p zh-l`t`brG(YDyyzhZ@RPnMx(L4nMe2(m52SHz&eL9yr;AQ{Yqrhjb1+WqwJ2ac*8| zX?bZ`PFWr%BDp1bIc51pbIObI7MGXh%q+l*(>h60DAFlXmZzI=2AqlWRz+2n7SW<; zgwhV$0+*?FO4=f6X=j^o4s613hFVb5by2n&f)f;~?RapTlw#6OzwgdA>EyEfGZ@KYxI zNdiv_2X^$WuJyNzWjs88W5TmiE(2sfQ7PkHnPj=NdQ}}p3v`-*X*~zed*B5VUX){Y zVh$}ul&D=>HW?ATwm<62CcL7CY~SzNLoyy-GvRerA|Vn@P=4LYdV7#JO!%uDBsqK# zx6W^x@Rn=f(4F(vH&i!n(3bZcyko+ol;H6H$L!ha;qeQcU%S2s2_&sMd2aK!(b@U0wi zQ22wa- zh)Jk2k&a010MmHFO(G=O!`sn*ZcVj6IGuk)JSOqV(Z@z>1K|i_O%f*&%#A=`gLZ5U zp-9J*E+*+3u!+3dit3vA6-`Y?*Ed!k^@TRo&(R7-b%0pAM2rJYNT*WDyN0)7)Q zURk{+TMtWmnxvPj-*f2&KESx>6+&n#5F+<{{}O$&h2s z?4;V9`0TiUILuWAu0v;;WP}`dOuMsH8}P{BD3gqq3=VW<(5Z=3GR7ohm5K+MM z$aFH(B)MTTcIbeLRL$XZl4p|Hvirccx(D-0GS?)3`GNc?v{RTzPbY_(q(GvL$bi~| z!y$@HQXEKK=PfRa$ZyC3QsN<{CMlEHJ0YteGTYP#hSL`8V-L2-B#T|`F2u&e0g;g< zCRrNr-hh|aVAA7M`7){UBRZAKI8oW*wwaWhWVvf}3k^3R^kT}d*lhlL1b)p`Ev`Z|A6OYI7jZUE$HldN|&m2h!}5Qdx{W0GUtOeMFz z%0d1{lN|3_6Nh#?(Ih9iChW4Cbyk(rY!iKdib+nD-G{n$w+PLznp@Gd+71Gzo8$~h zZ4X^s7W$hjsw$c*GzDjovpwV-lWgi_lI}!hduT*z>N?8!VO@HNp5tNWEep3R2$MIRR*7Vx^V`sN0wL6QP4ZsHku6l1U)ZZr7A;lIsrD`Ml}WyCD=vj~{g+Ap-Bx~P zb+r_1FJeldZ%p!E*MuU1_^{D`zB9@9(S|iAD02OSNq!8>?!`GR&8tHe63AZivxn?6 zDX7{udfQN_Z0C(oons_5P#pB6)P(aXBXE3YpE@4w2SDSgFsTt(QB|at_}3x6fJ~^@ zq%p20>DD>UM9$nE5;;4g88kIl)HY~a*h{;bG+shQ#61TTnouB1quoq@nivShs!L&Z zVMt}Xm-aAeQrjpa`}Z_0Vw$92{g}9b##yZ$xs75t~|v2yURFV}W_;L;|yBl@!lU&M%skcUW@%?Bu+| z@=MD~ll`*(Y0%|IxqZ?oSEkApVdZ`mqspr-QA{Q95@pJ{+yo!3 zAn?p@nQ|WFZP~PvKt?BwB@y~ah4rP&5}_X?CPIJN96*hTZgb17Vzi~M`Y0=5k@TNz z(ous*LC`*K7dYW{B(+S+#ITuj%vTgr|cLZC1l9!u5Kd(H0*1>R$L6GR7XA$Tc zlypIT-Kto6COyZbo9MYXJ=Nd^1*P68ndhbFV@(kudnI*ytF*k52!rW`QY04rt}d%sC08w*R@b-GRE0eIR|VWmztzoOl?dsQx(pdD?xJ^l z=shOAS2|orL$_;2=&!5vj(g;d-fz-vvZ7;uIN8ZzwwrW^93#%&4U%6KgPiX&X)AqD zR(_IVWFVm+C@m?dDRFc+-Q%GToAeR-XuzB7+c*MZlrb^iC4whHrnIyy@JcLCnDj~d z6an5;eT=29{6rW5UN3zH$JGHiT?gD`Fa0xt)Z~Kv!u+!2(W6Fk?6^?%M^JyMC$8bSh|PP)8H7# zNfD&dcM@T$9B3N-uZ%d~5?GvC;Np}ABXUfKL@)gx+G+SK`6B^Hgc*|jpV9boN^`vo zaIT!@WfXr7NrW8vdc;g7kVb}y=6k7~h|C3{lgxf(y-}x*5@eNJ-#c(&<;ux_V3t&C zp4885d6>gYDS$4rZs?2veuwPM#aVS#KunOby>7B^y4|FmQ2LH713m(=dFe|Hz z^6`3MMMEs}0mB&2`kQP38;FyC5f0E;ffN$xnd-!nudh#uV}saW4;x~#6dA1&BfDS( zTwT#rMqZNG$KX84#)iFk+%`xe4Q@U z+~77p{jQQVX~R=ZHjPaukZ!%@BA1@$)mPW~tK>GppiPG^OPu#>E=zPM2CUzT*yKbg zmhzv^a;5xdsnqyj%Ksn)wFQ#E5|$fFma(}e%V+Zl45+EET&wo={W6yByFzPWb@eq>#<~;aWhm3SN+yfXeY2HLMbaX zSs7c1NjjFEH5+o8WF~=`Ur791*;1#78Cg~@Z_n0Ej6I9lVIH=`WJ}rM82y3}x%IVZ zPgX$fQmGoK1q=6-)F@CphYuo?g#kOlWJj`cnbOzGM?Ah%_pWk(5pOrEFxd)LiBTPA zQJM0g?l8lMjFK+bJPZql=^dsc?M~!qX4Ba!ldWde1p0=UnPYc4b|i4+uQ=I8RcAQu zq>PnHPHIe6%h0HYOWzM=pp77K_it_fon%#V*t0(&vsh*$M1K zxfoehl~Yp_;i5G)nxC|#a-3u3u&0>pR5{MD@NwLS@vze)QbL@qV%cfzOcMk4wa*iEx+|Uam6P)$AH;|CptN$S~O%A3E62kfQP-OcSm% z+4by(2&O~Q1oh#X-z4C7SUk2rqb0%;nGG&wxruN%Bua{ux%ByW?mfs~| ze=yl?>~^((TSk5-L2i()t*t2at9=%JxiPUxx?^EQ9`;a3VJky1hSohMdzd|cVr@@U14si>VKu+XTF5=Evom4< z{eh{#WzydxqtzY{`(xxD&UXBxpdRlO`kPnRSH-f&S-O z7V=k<{f)hel345CP%57ibPFkEr0Jl^vbRn44trN_PHRSq)yl6`IlEa`rQq-f>3yGl z;9(z{>?66%;>bI!QlUvL{i=6ki|Xr&T54*45}};^-DLk@pAZnLvLZ0T8Ab{#8m73G zSdQZAGxoWMePOaM*;j!l$&thnV6}1?X`*XYK{-V9)45Z9 zBeH2U*ISM|z)f6sPeuH)cP}%t(~Xx+6UoRb?fv3o!h7#C#Ao~Ii0!>Fr@u!CD0?F@(QHrXGrzAqNWT!>4vraaTmY8ssKXm^yw93l*mbzw{Bgh~p9}Mwq zJ`M|9K3=8+kxE%A*Ob>w5;n+%{bM9W$4UZ^qnE|y51(oB zTs{kHW;<|3NY&bh8Tc$@&(wT>xApHhKAX?+@VO=%9(*1X;_|Y`Jd+O9_xl3ANDVi}+%k z>{iKdr8Tz!kuU9G2_|WLsp8K$eFdH;digT>!NWj6iiK2uq{++Wqau!fXllK@0`J>T zjlH~*KwNWmt-pMYdXyol@tb@lM-h6E8fyX7m7IC8Y#XnZrm==V;eHlNOCvAMOIsAf z;(3k9Yq|VlXtVvC1;sTlmN(FoJ^UyFsnI^Ql_u0=@@C#5vo9@Sk(!?`&0z*V+T`mw zW=o!?0K0f^o?5h_Psqc69xvaB7u80&Sb8QuL8{1C&L=3;FMkj2;*`}H}5stA5`&un&lWDb$6b$$>|7AXx4)<@U$gC{dOimAhQ#b zKMocvUo9!u>Pu*j*y0uOk;Tp}qahb4Of<4;wS1S=sg2ke4IMko3H;$VT>R_Y26AiH zTafa|J*>at;%w(c-fLA`oBDVE&NlUTma{&VuedXJ;qQ%qxkZi-Kj+Fk(CuY_Ut-C) zGn<3wVKl0i?>B^QQ96*}F$z`JRo1jr*&k|@Uk2<1QCJ(bxznA%rN5RZc2bP$`@z;4 zp_)qwZ1}An5MV;vH;kI!^ER5LGLby0CRb8++;13Xms#S@P8@`35c_rngc{wGybIZ$e z=9K3a7tP8q%P%g%oKIy|))Hot>vmwQ{2O)HL4&HQ)|qFp>?v-*_hF&JwG{8HsKpK zp8oypYdc_hn9Yt)%F1dv?8}c>e0L;}Pg{cR( z&bdQAfs^4bGHv(u0{?ha-=XUmUJz zZfR7#1CS)m7xp{0ZF9%A*<r+k__=-lD#hHa*WjRu%?lMIQX3SB|FX;#CXc~ zhpOu8X~f}gO!<1;XEse|SuN@+26{^O8_}0n2f)Xy{8E=Zk96gSix#z6FE#5e@)y<; z7f@#RG~(=?^i5Oa*@WMrRsjBP`kInw* z0EHVC_3A>+&h`7%BqtM?qNZNE{h_a4%;qLCe%n|x&0VFC5;G^y$*lG+?C(&-sRS#A zg)mW<1OZ8PiI3psWd1iu{b6q3~wO-sqdgT)lk!Z%+wdfNK1BC35+;^ zxljyFxB;A(m}yO<+^hN8^sQ^X`CRqK(g3#)pE05VcGk1T61kN^&s*^q!g;GAy9KXv z;$vyBy~sU{cFC`}E#c&xFBM>sD!^=FTc7J1IO3#bGCs6WrQCW+q{2LQvx!zqz8Jd( zlMynfu{4%hw+VHv`I-~2evZF0?ysd$xEIY_O+RcHtXr_nO9R&klm#pSxPgqBi+;H% zHe)#$SStFs=nCp`yj=8;_OKl0rH{xcLd5O)*1l$%k|-kd#zwbh zrjn>Bd+1@y7m!s-D@9y^a$w-g;_r*{Qo0C${djQqO37Tzz_G&KK(oD^qqoji>gsm_ zyT#c@x67m(QmuL$?8dP_$Li3$E9#rG!j>ljWbH*!PTg6A;wwYT52}(CPjA3+Yy#dn zC5KS&GQ24-0V*>!cGxz*mrP+>D_>cYVxU{oAL11O7gF<}b48Ns5#ZK>?y*L06lEN9 zLs!>t*osi74+@S=l+?k9@j*utRO(w8HAoq*TL->s1V?<0Guw}s)FXk{%%!-C{$}bL z2q#dPgA`3mEFF2LuGSv9Phm?Ndw{-zmcFyr)IRA7LI zvTSd-gmQW;GrOTSkKVKOfjTP00!nCV)4rt#|Gj)T?Z(Qw2v`sd37Kx^N3vDy@OITk zHBHaRI5JLK|F&*AF-1!=Iga3$$}N0)d>)Q;9;2aqQFG2n+*cFwd7R>JPr|se~r&4nnO{v7x7~Q0uRN-k? z(S<%(>Ls+@DzvDMf=khc;iCvUL#>GwgXm=$9F_6UZ!!M5Kkt80snAsiWS6GOQ zlpG@41?Mn~5@YD9grPD<|B7z=ONM+}mg{z< zE`*e3-TGcDPF?2IR-@ge?B%M0uNeM37YAG^4xG#Dz1;#1z8O7h3Z#59=?7Y1H(ot+ ze|~!g^NbGA<&f8kei$jOD}hU9c%pnAVKSktWB?7+GBi{|{-j{GTL>;;wYm_!ZCSN_maSLU4r0Gi4xO2cREX%B+7# zp#@H%T2O{HbnNPZ9dr-$%xN@EI4(RL{31@~HW>Ej&pqlkfK?a3-C-`d&ek)_w)CsP+ zE4At?eOKHmLE~N8Q0*BnjPAsrg+cpCh5#s(xeF0bNZfyiHb8n4B63sEE9cZP@`xAE z!=X$XlN4@3^P!RSh_1YzpsI^Q1gKrcz*pXtq$dp_qF#OJ^VfF6k0-+6EoHy8zwOw4 zCp*(0*=nt~C;Nzhwp}9!1$$aJf+YvZ)@|#7a%c~PhTI0Lbnrq%AOHO`Np-!1nFM&` zc9QlDJn8%(L|RXVaYTq6SvkMPb%t`%#)VRNRH-Ij_$P8U=`@;?D48;d)a;FIR%cnk2f}{J-lLjo6}JA}vEecFh|4!O$aXE14Yx9DFPYs6xwckl}f=euu$Y`jP$|IEv#%7V!^&LMQ2ArGr<_0uLLTOnBl{GVP zB-3pf*d+oh0a)mk=kIwJEcAe|fD+1th~ko8*sd$uR60^=1?mf)(au0HF87#{WK0fu zLR8G;SrYFUbqrfI={}9L0?Ep2yzdYhNP;`Fadj#Qbz>MdgAovgyytvf{?uQ0wR_sD zbwe07;@<;)^}#>fiU!h_f3Hw(&XB9qZqhcj$);5=Tk=|6GmJ`{p08}J1d4EQF{ej9+Z%_h?+%`pD0nMQ);70m|2ql&CP%i4zo>#@1% ztT7(;n4I6nos0XOy*bLU5?kwHd0?2Dw(H1oZ&vU6n-(dt*M-ermmzQ4*dr;Dhg4Q~=sViQ zVu>b2YHO5@*%_~D5ZAL^zEc^C!s{ zkuiIXH_7$|+BFhTbwb1PGNGznUYP{0&b7vN!y>|SSIO2cu?kv_MPr{j%%`RkY$gIxCXJ`jSZ;OQX(ZcUz|sFZ|GFbqzYwuyySJ;Z0F6; z%3om4$zQsrnSZoic7>8tLnBrsFTa_=8Z)Ty7NcV&>z>HU&IeaLu;XLiSE|kA%EuQj zJapK08b+J|RWz84G{dbi+1vQXHV@55Abs|}tIB;h>0;CYvJ7IJKmsXi+sN%K+}I5l zY{o)#7q~WA2ThY(JIQsfGK{lua!H1~qmg0G;Q(9(GLB$(l16^Y<$DU+?WRqcc|}%f z3ZSGyw@&^yb+nR^wqWLddfDA9El_7lFXcw5FO-A8-=4+A2gPP1#QvZcK&B93;ZAQ)z*8_-TwGl+)N6by&% z7-n|BQ2%hTmj3I>8O0on@_GAH=M-M-^O(v|Of!Le?EA&;4}V%#s6g!4d6X9y=>S|0 zYG|*1IULB}%#h~2YH3x{1d#-uB>o0{ZlL`{{V&Duto&kg&j;^tgHJAh_sW-Prcc%M z`6U@tr4>dB#J!^<43c+C)+xltd~)Ab)dFOJC!nS}#&il>u;eZMtzc*F&PL{vRDi5D z^b!kGszb`DUi32s!De`Z3o=_K$7Y>;jBO&FO>z>X*wEcP$T$!QnxH{ul$0J6Zi4TP z2tz&bGyw-Ak8mA2MQIAn(sKx2Mo5Gqwx~h5Q#Hv5q6k{{4a|CUZIM)+m{>fp$K`Rr z?O)syY0uI#?oedBUn;o75$74VMSAr>Dcxy@$zB9ZfWT0W)bq$(wElIqY2U! zXL|8}){zp5c%0%_I)%=t1d|R$k0~J&z&jB zR?h?Iv8L+c7YiyzKf%THc1H5(8byPF=)Q^&AgxHWZA9`v;F}#nKx+q$`oQfnyjQ6R zrfXEG2;i~T#d1UX553NilpgKvb3{fRF66RZCS;bT_{K*FF-NW{ET4u%__PMlvRC~fbN-}s8 z8>zlDer9%7*=l=*U`;DnwxPWmn)LfRf~_Kzj^bPFWOblV=@A?!*LWeqpSk8=Qcg3$ z2L5sjZv6x5WQHRiA25Ox*g6F2)VcRQ#C$vo=Oux2&wvyr#65N8n6(AG;O?Gw`uw@) zW3gh1+&0%X40_YL_dd#eT&ibwX8}DRYlKqM7W0G+J+MV^S9Eghtl|r`5~OO z%`L=(@XD@n%tnZE6e6O#4d+w`z8k?DR-t>~Sp~ieHT)uPG@}F5qjb##-&GiX5s0~a z>u0ueFZM|VH&(Sx(M?VLfu$s=o>f+cbszr84zCo4o5a!ggeloZo23Oq>#dZ zK_Whf-=>)eQNCQVzJ{GX?QlxIBs9HvA725F1r#%pw=m&`OsBJ*wdm1^F(U0jF(T-? z`R#mp-n@CfMl-;VKSg5Dyd+R(cU4<@0_ktuDd!ty-^Zp^81#A+kB3`mzL`pLX+4X( z^ZM|~x1&Kli_=YlrP&s}A;>S5C0$$Tbn`6?6L(>=WocenUumbOkMf66_7Srzzo4?! z<^Jr~m&^A{0e+$R1PR0CGe^LrDIlat@z;h8^uf(R#*hK^cDT`yEfU0AgD85Vi>Uud zc!|)RfGZ|+U9;kXTqXKnLjppsMp60zZ~zi9fDmg`QNoTXJ&{Emf{GmLs7iu_dV+FM zylLXzsE0FSx^OiK`l!4q-0x61a^%F5ecba8-cd^8IIVp;*3fgxA7!cg^F&2Ikw}AG zAZQB`0kA-UprpC^EmK&*oNbi0GAwH- zC1HM5#u>|o)S>}>l*bmGCst2{(Nt$t;#|;~WgRHSXf_=;zH(;D_LinQ`U$8u7K*oN zP1POuj>CUOsSl1N;kq5&$I4M`zGZc$Gf=QU;@D^OqyL?mt8lr49sl;yWQyM%eZJj3 zqVOqIAK5MKcIQ4y^oi@429zbjOyZ~d`v{N(@okYbxng+TzM?UdrzA#P>Y&m7_qvD< zDm>um*{;W}h7+f!tSyy8WabFpF41k5N0<*q4kdBq^61te^gj9x=8M|9a2LDrh=wHc zZBxA5r=4lS7Y_O;*AV?}wz5nYR}94$+frO^6kh$Z<-nF}5>BbIwXSfw69-3>j#Xt{ z;1F)C^1Mfm8^@<4dQ|s3(Gbuo0oqbA76A0k*=)@HyN-P|S^~~Le@rsXpRubY8tPSk z%&Z75|3Dx$&u=m3GEK=o{)3!w=XYexedC<%^t?;d(RbdZJEH9EmjH!(AH`A+fqN7O zvg}hrVGECWs~C6w?1RZd&&?N?W_R4lr|J^fP8%!#mJ}?n>EgT2)LF<)e9Q1BPHo`n zhU9n$-?8WwW826Zir%ho8l!4ckM6aHL!h`Hn>uaC6Dv;1H|(9NV6>)*K{E~%&BMM1 zods6xhszzEm#Sm=KW`{s;DmwtRM;KPq$W&`qO6j!Ij`gk3WNf#$CmG0E;KniCky#P zqMyfQxB7yQI0AUUP=#z>uM%yF%vj(saV?5db>iUl`tyallcu~}}k)to|Wt?}W$V5eZkCq_9M7es;${Mb(U_L{~Y-mpffAanD)s9SijC2s`0$I0UdutBu zpqDY4+bCH!cT{&S#M2`aYXB#EAGF%KNG$XfxKZxn-Qb)@$RG7P=z=DE zdi&_y;VJ`!v5hIcN=d*a>36f_LQ^gRA(;E@x6NFq-@3!{>Y|16 zAeBpV$q<^G8Zl<(8(3L&PIf@5?xoex8Gu36FxBY;GIPfY$D%-DHTq&599v1K^(cg; zC>MWMO`+8yJ4Au$iksRR1L7S*qA zc_mm~DrV&C-g zHx^Paywo_nEx+P!x_5JrVMo1)e!RTCE8)OSu*pHND$i=$m7T5KLYhKc_lef}31E&y zA=#Q+41XYf{bbdBO~i@+6z7;4MM_BQP$(4R71DH;i3Nz_2RVPT%pCfOemD>8H(C)) z_{3J9yJ80UoW`0H#~C8-!&TLu(`s&=+HHR!qO5Ls?te$c01yyNGwsA$ryj%kFv;D| zShHS3yF|hC3M>J7v!CE$FTrSABiFGAA_KXXAYbdM)#v;{+5Ltp4& zk@_FwD}h{C_`1K*s?xsBl^nBDp6Rd+)h|8k+_=lAH6onDHRx z64;}&WeFAgI=7O^zV16pq}&5?D-;E%zKTY96@aodY!nQl6>XbX3y5~`GJ^{)0`15Godv6U}aVJ zEWi^%doH3D`|BI?gtE(vJ~a5|9$TKC+)B`e2Z{y;+bKT0jWVjXwa%`U%X_PP76SsJ zwt0)muuL636yugD&`2I|>FFE7c!*j3Z z7OI5i_T9mbHGpdi0qw?%LW*;z#-@*r5;&oQgoE9O={XHG8o8mcwHZ~Er?9Dd>S%cg zUY1@pLnmiM41w`DYwr9btgl@AM5vGBVU($=OcA-DM9@Y`)u6ewtR_I*XDOyc@Q3xO z4fF)4^=a;9hDs{sHemgvyC&#anEA#&c;}Q=a_dWa|3r|YDA|v^aKoK;dyvN0%OYEJ%3C>Y%3@4#lVg9=(}!Hl|$B1uoFdolKP(yBt}zcOXp zuy}1nR`rfrLI`xAyW>6%h4cy&23lI^%cl5)wv+AERnZScWQVbHk{~gn4grGn0%3gh zi+eWc{hZ~zhTN7Wk2XdVsD=ecd}Q5fyZ)y1QY?v{;16tBs#;vA(T02oB9S!4u$FPR zl*6<7NHbQ=4%e+rx%3c?b%_ins?iBQxYsd*^YcZwx=1tC@O(AgLD?=GV7k-~9z#;`^4GJxl#~&7cZK!#&04nyo2>(0r?s z9=PicwdgzOZfHKeQ$HP6(y!6^GMU{#0fwmwo9E7~VR{m*D~qNC?I9R-HlyF%1Xg?O zNvInb_O0E>S7RGkA|Q0_g&h)5eN3lji_0MDLu<3H?0x`czB0H+bKuk7U8|&7zvR zmgDwwyH^44P*;YzN)?u(vT|+$bz@KhN|*!1Rt&M^NUwG|_bG zoxKmkRj=Y<;ER_e`cdf*o1>&b)IZD-JHCOVA&Sf_*3HMCgFU zb&cf&|G7C8i@N??w~OhQ_59NTgZOT5kn0`d23=_OxHp#T)v<{wgg%tikvHjA(cFNk zRjCZe?np)t2QLJbwgrWMmd%El0GRvg5C8T?4#!mD7^~3+;JwK8ZPQHTmzIA2R?|C= zoU}(<5A<2>pCoQ$5wvaHO;nr^qu?iVrFEk&!)~8U+u2tg-?31J#Pt< zq3rspr-Jg@PZ;brV=$dp)nhTeZ%C-N&Npm@Ih&d#t{FLbNumbiddC^{C$K05Z0dfd zYCB(td3Bi6`5B!48MN&xIJ!O@(P7I^NCfUL(V^rQ5dZ9t!~LJo6x8`86rwhrf)b&A z;W*bpluEh|m>9xci`^Y)oObLvGk56?=s-T`X0_6!z~m{jJjoceQB$bl|p}{TIUiSK|4rX@Xv^T>WWc?faMNXFAoF zQRj1tMWT>0QFcwCTiB$uWex65<$b*L3l_y47EaIAz}y`X&Ui~NePz>w zHBY^X9V8|WADD%7*rYfpDL?pT+J!HNXbpQddH)FHws?V}L z;CY*HJ32+(KZRw_5E6qMDzN>8+s`wA{vEO0VY+T19Xz=xmsyd{slkTHB$w@b*|(_i zZZo-9m{9@1+WEl_9;$GtEhcfhqfbXCGPhcwXNwD&YnUc-ZajXFrSBTsnPaH64h+Q+ z{X*Jc_INxnC>Zp;(3HREsecy2oZTQv{(=X`mNJIE2JWRR`BzXssys21oOt8J^=W8- z^>@!!+Rax+*K2CiY9$a*Y7tbOq!o&vAiwhsyb4M03-ywGHUwrWb8;1)O9U&wcYA%f zV0@`Np$9f~4$2!taGmDtQq>{a1H1ON)-$WCT=&Wn@JEN%2K^geM@7HMN}J&d7AAA> zYO<{;2Qs`wcvQ0p??dIm0;VP-Qasl1ODGilH7f z7Oe6Ck6-ACMupr1r1SF4a$Kvd8>Ztf=wBkEE-00YR1FYA*Tl2e5DUR0>^w8FxUq{i z4tc4OF2KXLMi=DaPiK8zpuTJ!3}tPiT|%D^njCwn-Z@xON}E@{3x?}=O^t?ygUA4| z$%I?>B!0vsi}-Kf>Wd;heR9;@%EWSV96fKh$tcVT^y4FMC&YBJ5W&R-5zs?Lq0H4y zBv|&t(74r-`b6tcQX(mpn2{UQR^qz(8poy@|AV1KE06g#t6UO)`*s65d8e(Equ z$tWID(QbN`W6+Z_kfz_AAcKK?z(KeqeM}h#N*Ra0uZTtAylG)C9?Y7Pb;az%DRTsv zj>SnO>na&}ru{5HSU6<$^3(+B48qDHg594h&M=7}=(@6o(F)dHnu4Ci_xxJGf-61z zM}xjxj!Z1$QYeQCSCpJ)VxT)dnAu}i#ZfqUMjI~sz#_k zDb4c#I>sooY~xGfK_xfl_`!4naIYknu~=mpRG=Y1h~UKSKw~k04BxdrrmpS{b7Vks z>+CNXUPFihHWAxLHx-^}4wkb=Aq;*C^~?Bv1d)}mx%eb%$GaH3sJRsR(5&9J%0_eG zfl%gdR(^oobR%-(6Z>OX)KIT$eW1>AV}@t@65iZ#}c; zoP0vd+RvDOHj#ZkII7noT3J&u{bx!) zxzkq<2p(jcVA>^Oi0vKubGqG>+S|d2uAkKw`z@)UwtI8%GcxStGB183z#m~ioxNTs zKeH7d@j?)x@ky+H1*v}!={{MHS)L!lY?sL~$OAkE9T?a!kXZ}pKunEr-~M*Ps2kbM z75=pCcO!U?M%*Lz{Fe3%lY>dzZ?SuIEp&qndEJPBI@ZO;FXq{7*hWnaEg6=ESDdN6kAxsX7V1L4u*b+S>>;}`e z2_uaD9Q@WBOP)w#pvN|<`*&C1>sBy*fZS|yH=G^-j(~R8td07DW1G{XWDhK76!vEy zkbN$1tAvhRRwBSTlY9hhR-TeH6)jgf2>`*GBr)o4?0P_xFA}O>=5ifa=mjqk7ls zFRnwNjS-k!*mg;0r3d)C`6%!Oj9O>mC2aWLcbrY| zbZz89@vq%0;=%c%bXQC!0;Fz$PqZdT9hMWpHPJrLt7%eU%EHFcWC`XNiONGhtdp9; z&e1BY>zdMV(GvTPCM;U^1d>jbVi`OBfN~fy@KE4UtQyrcLwd~6UJS1 zR-;kf4bx!_YVVi1DO8iVNqiPlvjH*#O&!uzko(4%U!blKK<7Ykurii@FpYXrSclh! zXS23Kd9Sw5Na_v#^NbNlI%|MX8-R9F@uMnQOHuU4QjSf&zThRCX$t|{B4MLCmEp$k z+8p5grFq7`KvYT?uirs8BUG$3#EoXnw>TjC3yo=}T2IHqDC&6l zfB!OKL_%VNL5G}<)$xrQoxn<%s70%tSTPLS2e=WDxTbz$?cwnO(`AR(L>Qyt>8-FT zHpp0~H!b0<3mJYzy9KnM%2%~)eor8APc9l?G-!mm0Y5xI%C`ze65pYW6w&r@($1H% z5QKGgo|kWmypE{1ot5ZB9Ztwx`xzKcIE;WAoN8$1W*2!ug|R+K)2x%%U56Qh*U~4w z$C+mM2Z=3;&xo-Z7#Zcw%(?;nFqCVnBh%EfeIJTulNfC}inTuLwhL@T!$O3kJ*yc;EgA_8w!1#qfSY5R)A4k-`VwFu@~o+aWZ&aqjyj5O!DiX* zDzLn=GMaaxGDf9l$QHN1XtZu6C6iz>m=Fs}>+gZh4=TC>?sxSG^CZ*C`%QcF4&ehN z6IQ@}`0$U88X!prUB-eXHM^8uB@`3k@s>NaA~7aDtB(nrpK z?8n^F=LT#0d;jBtutaWR6!0qdcWs}ycWDo{&&#HXZy{_)C6*@T+xKy1Oz#8l!qbST zPML_npY9vbos|?af&<5tg@jiUVLr>oOHVP$lCnc(WRuDj4RK0bIC;!^cy}nY6-L_A z{VjKmwIAX`vBFWEw=dy)2Rat54HI?n_bO4Lc1V~fKu$!4j1UA1FNsadgLF`pdJ0-{ zU4XLsQ7_(9;!ie!;_=SO83}bi6}4F6d?BJUt9VJfG_zn7!VHVd9822;gL=+i$^2cr6H!33|3UN&G-;L+n*_$-z*)X z<};t6-HDZ?r<@Xl#1c*@g$eN(q-9O>EenaGg#vw6O{`He6N zW*fmJ8-3vQ$`YNatQ2x6vcNeDx;)YU1kFJM;vCYt9@iY zPqQB%rN=DnuPhh_VWMLoevqik4M>v@CXIbDP(gAnAj2olnd->MHYItrO4Hh6*IS?g zBTQK%Oe4fF1Z@9~|I`cBuXV*)BnS$m2r9O+W1GbN4}@1r+4<9Bun$c*$n)LV6t#Vw7`1)CR(m5wf3X+19c=aY`S|jUPit-hOt!{o*B$ zQVY@lqKdrrM;d$MRwMaIHA!M5x4$9>OtPl(;z=7pVSLy0Iu3om56#H|MauD7<$rMA zu{s33rr_;YwJ*g8BpvE6RG+9kmLGXuvl1nn9t`@s3j|Lmri2}e)|Gtz8LeYT1BumS zAqnx6K4IcFnE`p1Ci&FTiL8k5p#YoGaVXtW%WYiM+h_bvPLQV6(wTHGzUIm&5K|?J zi!jMl=e38OTn}M5$$y15uq}W#Fxg`e4P_l6UxXF<7Svscp&D=?2Gwo4*nnKY931vrj8}4}u^kG$CU6KLcAoTcC@45w_E`nP!-Z9r>I$=3Db|8Y| z-)#~tz`W>za~my{x8Q;BKD1Z6_FO2SgY**ua~feJ)Xz(hK3H72$X%sU8Nl_6tqh#@ z2J>B>KD|r~+Cu{4Y@vgx)>rcdpVeTm!OYdV*mkRbo5e^woe9gbN;i)fXj@cg^)X*3 z2^c${PAz?!R&V1;&UX}?mfPx2wcys9U~$5EB40)pMt;9AIsd6AjEI738nYJLOL7LN6wf2mOfCPJ%^m$a43) zE3wXEXvIzovSo`xuW9iIpuOlrjyLn74i%O)@6nkSIW0A3cYpHw&r2sP4iN_W{PXmK}LNEBWMG{+W8CI9k+}aAl`V7QVwij6umT? zq;N9}S_w{JZ4LnaKsYp~m(J2~IBQQ!;bq$D7N@G!5F@7zzz{^qgoFZTlo;YO5hHaGM0SkBiDta^4+Z1@al}rdj ze>~71M?&P^`XC?DqRw>VD!GdPLN7If z^59j#x8N1ki=9zQ#VHAbtLvj&fZWx^r|ss(tvbLsW>jzu;eEi+4d%^sZ&A{qEk9Qb z)UFJ_xAVEkMoD0WKawI0zP{4{Me#6m>5v9>vYoroBZB>A9KyaFmY5OhSvn7c0+BvoXw(P78-Xu$43V7bL7IVhOf^jb zCn0@M%qw_-3Ei9%-P{dy(Oxt7UnC!@%vRSSZ7wnY2mcd?Y8xc*@TnjI1i04Ok&`hqtp^!iHQPXEm)(qMEabRIS*@6;_ zwWXl-uYE&n%g&X&`GnE7h(AGic;z`a(ufbhhj0q9@)4MFX~CA4k$kfL(?D$=Z)}5M z5l#t9^rj-igWndO>cW}zC&`o0=jH)*e}>69%5$J^aA}eV7AzSl_O6%(@#|j*Ms|J} z2~JBfOa?Z+D{+Tl_waPgY>uCbe8^smegBkicH=5*@m)|b`ao;$_&e}lNMC4e43(Dw z?%gu@7t8EP{u+V~vUyJnEOa8a?m)KgV6yJ`>RtFd*UBxwRrk8yANaE1hf@;s8}-%n zUiNCZqWR9`Vjw}r4En(kH5H<+gIe;K0%yQ`-6_(VM>SZr>x=RWaY!sT-sqdo zKFsg=^EKBDLg&)oL4Wq1s7D&or9~KBEE*okM{;Wo$Yc6ZhxfwJA58|(2LWCK61>o7 z_{2&L&4_YpMxqIVEIwr=#2F5`C{Ed4B)bJPj3F2i$AhkTDFRC#8kSCGJP2fJk_7^| zWfwj7&|!r_qWtwsk)H_t*IbFHKpnvig4@yh3+nL{MfFynHi?lVg!RsfCQxMCtFB1LJ9nhz530_{IP)D2xSHY9Uw&_qKN!WshKh zK?u$(+g4b{=?44_Jgid4%N2)1x&D(2v~gpgsBx+J%9T8v({1>wjx&<1SMTBjK0G4 z%5va-xlopfgQT;Ah;s|s?-o(K5AdksmR*xbS+`$#;X0 z8S!!-F3}0KrPgUJP?$h5a^eXv>{~%hY}glfi4Ja2PN8%=^whk|&4B1#6_VX=PKeA- ziLJO_i5EJHH=M2=VuV*J!`=%wg06o`V%8U(N_m^h_ry+Hr3i_Yhldw#Vo#rocs~Jz zxyr*r6?=CP0oa4%&$+hUCF{W6FZ#7ls!etG%~|)&hAW?6SMNZ*trzjVXWf?WM(h-6VXy1@%mt2XlN8hqUveccdp0J``b)#$b|iku3n?h4E9Q;k$=k2N1NHsI= z)s0uWg)I6tSGILme{BDh*KC*@w9j|B#rj_OiM{yOiERN_&9*#Y?4&n6HMcw$y6&X* zXkCvKU#Hj}vybkxe`XmpUv-q!b>?5aPj=lk-{iF2b#>j5em=QldO~Eu4C8#I?U=0R ztv*Ar-P^u@sQLQ;)dG_=vAd(|D8PwYyDEh3#Nhi>7+&@(j|a6=Yw5 z@i@j5&O1vnKB>(Xd^$|aU-x~YH@GZ9Kwo6kRcBY zWf7*Q4c&=M&)6H)){4g2h=Gd`V$|O?DhHw?;+Q&RNaTnM%du^}=D6<2nG4;02d@u{ z(!N|{&82T7F~-X^?pQl zIz~ORocR&(0w?9RJu+t}PyZlPw;=J#hNmSL$B_60YUQ;cy!we?t_bu2bJ!`+v(I17 zvCZFNG}Xpwgl;f!YzoGA16Bv?5Yq|<172e@I(_hQKe4c$W+v&5kG><^;0^umf!ONB z7!&~Kvgql(2met4Wu(R3kw_K#IXqZwNt||Hmh$23D!aD`JSC_%=mNT=Leo!uaL;4}e#O z|KQLv5tUwurW?x39I0}rSqjPbKDlJM*&a5y6_)0{27gu_I_(|6u^*CMmeTLk_^yX7 zG(81+f0GvtWv)VjTWyq5>zF54)}dye(2Yy%hzir7*fhI?Yh>iwf9FMAR z1lD%vGz^lejGOQ)2*97zr(g&DZ9NedvbHh`F5>x ztonzX|3$-Bl#@AO$0esSNm_^8XVpzWAW#VE@!1At>5E z`)YY31+_-{*JjBKD*ex!S{m3u9sX04T%c_b|7_~7EB?O)*17)K6p1`2V(K@3P=tTc zJp7>jaR1boB_>wyh9IT`XaSXTupoy5zeiT_e{q zKOWXfm?^|GmKx=GhG*jCk_*E~%1N=LxONdF=4S=EsdP^|fY{BY-g8c#62~0FiI^H5 zIw@TT@XHuEpSml32TSX1yf-R7-+qi^dwTb4ISY2|a)=s*?MD_Lycpa2@5t|b1z50W znMk+%T%zwbUh~v}a}UH96~e<4Ar?DoR%wqH?hf(ihGH3rgRxYh)B4D8M9@ zo#VKG>o=`;1df<)qye(oC735>Y+3_k>$=7c%GoPKsw16r`)C3s(;%ftk($MHqw=9O z)UK*o5t?o^<&sSn>TR5Khq;?-$7~ROhwRne&EHH3%S-0Z{gGp(P=w{1+i;xH!d7$a zZIqa;sfKk=uhB9EU=RfAm|bSbINGr z2w4#7f@4E|0p{xj5Z!TKkv;7%8}B>(ykj0Zj0#An@znPeBxKD859i#=8mFm!6>R_w zVdv=?%hh8%-z`RO^u+ktFh4$qM>Ew~TpkOq_u-AT`_V&IyX_#w_#F~SEu5xRPYu^M z4WowWfe<11_xQ>VNS)f*ruoAW^jv^U($OSWYh>G0EK-V-icJnntUhf_+POVfYB@+0 zmLjJLP-eVykR7HOH|cs3{a&$(*;Ga(Bid=vI6NFd;b|G(+4k5w(js*1M;5d`BE~EB zhULN!k~<}Vl6b8udy|Yu*W|?g>@LqSrlXfQDr|!-Jj5$_Gg=I6uNZ`2^H0%Lfll&ab`3DYD1^4 zi0K>!1Z0&j@#cg%)sPDmr9~(g^d0423JBJM!cqQHvWuWY|D_?uD(K>W>iiF=(SJ&5 z8?@*@^|S|Ch4n9@^c7UC7vt!%Vo$vqV zCilxtDxD`)T~*!5lWLq(=Y%Q?8*8sGWC8c+XnPX~UcUzjpMLZiR4ti}H{d{;wtTN< za9q#k2{b9=zXGloMSj(eks{K5@cn@ z#Cz{2!L)}J<$ z5lvA%1bqb=nGrflVm*_na-aKAO@JdeD2l3SQi-7bRhLqD=%7FeTOTAMOANm+qpQd4 z=XBEKQU_-*PsmpfZ8D@1Rxz?oicsX-;vK4mgi&MPk4P6lm3nv^MGJ<>Sqqo1qX@G? z*ObTK%>$%kLV?jKN~lLW(;N*-W8qF2W)OBzkgJN+9GF&_X%5=z@Lma2Bd!JrG*F28 zGOqd?2lMGNh=BNbDkawVA3`z_ni5vGCT6iS30bI+gavZtYZriKrr)Szt*76d72o8x z<_gW!X-2ypqyqKwnx>#d%jQ63RvW5=*(UDllK&VvDF-lcqvyyr6WoOjN}I|`ty4EZ z&|UN^yHf>SGG{~BC*o@$4}RSg3(L6s)gF#(K1JLdXj1OQkS4E|3*@uLDTJaF1S8){ z0FP!y=sLvLhl-0xSC_{$(3Hx)m`KOBDhp$kNpPhCkqO;Jlsa3c6@c(UCORkcM%ca8 z%m9o*pJ|+g*$JcX%Mm0@vL=lvT%~qW3UFp5RuwfDuB6D^r(9H_WEWQRnfZ~(%W)CU z3%evDgDIO>PrEU)cM=}&$( zh0b)}2f&gzGKDmbM-V+7d5UBN|qNd8MbzEFT&6U^EzVf`^epjypo( z%FCdG#}1!jIiLSAr4^5Z4;@L#Rpi8tbh*A{2l`gIi$FhO(MHD^dp&H(jemJ#k6c8d@;Z)4PI1* z2+ae_ z2hklk|2(3L<4XQ89oSKYS-u3jhz;4|U6bv$vWU;qGBx(^^`5zI#VSeCe$s2$Y7Jq70 zEtNlDoR!82k@&YtH>J$u7M`wC$mreQr;gfWKeP|hx885C$|6pzS3cA@7 zuHgvciN)Rg07blLYNs3pub|wb<|4${X|odMq(moy%a?#ZzZn6udH%su;;bESS40Zm z{E?cuML=f6LP-4d1)>SUJWAm1@6Ak5GDxi$b7hu^W1jNyp5ug_<24n$Qa)T^j;7!T zGo^3xbr}%>3xRW*LwV`Z36tHoWIzpM8#=8>_12NAehozWnoW8IbwKDQG$>);4%{P6>)VF;oYnwJl5g;p1jmPJ}4qZcGXtngyf} zvAoC2pzL=X;{d;}^`Pud*=ol^aGCHs+7;QLFKe!ft;!*q*MJSl&IeuvfkXTp%B%>_Nds^sQLlf*}1$V*)wi6o72J+p{(qy zyv*k3)erTi9K&=GI>{m|G@bjmQe-FOqitY?Nkg8jy9m$FvnzXB1uViBU*7AcG7=(2 znO*yuxp3XoIOg8uJMdw#@qeRpGAO0O^+FIMl6(kJp(rgPC+$P0P&L%(jfAujDp;^h zQb=kp4jBMS=~qV$#iR!mvpOh-WMgxnWSXioV4Df~s|TH!XZzzZHsDk&5e#yqDstS& ziMXZ*>W~P1qNr0w^OR@COU?dDXe!7os73Y63aG<+-Q?y*P2tn@+3b6V$~sOB*rBZvp-9!!Ugc#X~0>2_2XLdkg8vCu-|d ze~VZrA;^>tQ;dvU+d(rl9vp4pZxz1+jjmYT7*k!n~+bQp0G%Q?me6QGL z7eoND%cpT~{Ns62>J*`xV{@%Y`Rsn>n!}U4#<+GcjQQiC6wyXjRPZnghlI^WbY4DT z6W|FZb2}i#Yd0KpZGJt`x%;Du9gW4xgei%=n3KAe-Fa^2Wo+^cXGFL-OZY6RmfXmK zNv$QQ^=Xr#%pnE_ODoAQxKidKzot(YV;s=9UC(K`9XosLKmpy~0wtQkFoJ7Wk*9YE ztNNy)MaHBat8R**TGkgqAwSHcEa6mFcvom6=alhYQ&ALBM&IY z1cGta$IF?;Ck1*LE{<}M=7Yi~y{E{EX<#@q&dmF}F8-z*)+XNCNCMi4a^RoRFAV@d zd}SP9Ec$n=ghNhNHKdYE=-+OXID&$_EMTsTFMV|X@tyZ|SgtkPeZm4fyloo28+U$n z>)Hvy`y%KHk3Vj6!|M7Hr`hMp8v-2S!qTHsfV>%@>wTl^-{;KZ`Pn62uKaVn!(6Yq z@tq+js^a4jopUkEZ8Ibt1qBd(N|O!s#u>QT9jnhN z`|=Wx^s?)%hwtjk@9CcJJyBBET~5dE?O>+YKhDE~A&d5CdE=cgC)Z!Wp)1UGp8MVV z@=QkDr^_dQb91+Xf?Pku^Xei)ar-rz)Y3~?w|Yg1i2SAw>b|}NZTbmsD+e&eYA4E- zxv7!PfJa`_|2Rtps4FEEyg9zSyt$a(iIccOdPKyUr^Ve3gGC9SNnXRt4RlY_o14a# z{ZfOxGCo96@E(tDEl#&Vw3!VUd`EF<_RJ(t%Lzu;W%|4tYw@(&=UTaCQ_V#?@anen z4IVu}0xl`b#L9Jf)eoCv0s%Dqt=_>wtji2SlSOy)Qm>zbK7Pj%wf2_T==9cjyCzyN z?RdE+Ug&!0i{r$zkivEY|2W_dR_q5u1GQ@3Bk_h|@#r(}&^Z>gcU#(yt~Uz!p*)jA zaKFEMkG1mY1@nb0_b&BrUR_;E&J>d4{aZ=f)wuJHE*FswnC@PrgW!))9ML$X)z zvmpJ(y_h^)Lsh;fCkH$>Gx>?+0v)@j<$TV10NGW_=cVl&u9M;UJr(HT*$TjVf|n)G z&H;jVdn87baGQ2U5w(ZVCruchPgxl#utqbYeauwnZ2I33ug2{hw?G55$3<4m$}qE7 z*K?b_wKwjM(Xe!j<)3O~@nMdrzuvLFP?*6PE&G!oH%ixbu>h*YruH)KTy&fv$>}_g zQt9jlc^*m@JzxT2V)X0pZ8fv(89CWen@gx5`aB4ND9Ex)BkV=!B7 z3$1dRz^uO6?gJA0Mos+;Mm}}#5H!!jn6FF{0m1cFNB*c^cQOGaKn6LVK7T>sR zK7~h!$lo*|2d!eW8GhW6noi+mW+Wk0(%L*Ith^(wytPMwaGQ4=5UpM8ct-x9!{2+@ zzFnJLNFcO)jcEfCojAN{Uoe7Kj)O9IuNM%HWw3Og9ssm~>$K2SSu9@UM{Ah_`U6iK zp_kXpPrLLmUnsllrldJOw0}n_=^B(V-nL;^nwXyo)3m>hYL3k=M($~Dd|`SuO?PB4 ztOb!x7{^w{0%r}2g?ZDWS;u_2;LM-0NlhTU5ltOP3y0PRzc|-uu_h4J(cA`PiZN5= z1kn1cEqDt3YX_?(XX>MN+q@Td*mUQkn~_;bDU z{GbI52oQ)2GZ49#PLB^wBFqW!;(v5eimRFrS^i<&7tfpNnYa9n;chgBRtLgeS)>Jt zsIB?~TvY&^tq4c232%|Khu+EE;oVgU`~?aqRRF+L2lo9jfD#0o{=nJ9f?U{!5ptG? zs9?fb@K$aPRJA3R+>lDhB5Xk^mIEXD6cKzY1Gl2^V?M)xr0gA`Vpb_@j7IR0Qslre zsF7b8lF_A1oCCW**nvV-xa4ye@pzS6}n2LhQ%Tx#i;|pdtNw5xsEANE}?) zy_}Hq2$^H!7ihv0X(GC-BDzVy#(jk1pSOM8?M>b7$6N0oNl)N@h7eknLP{J-pOfjA z{GP8}s$PYj+n8HC$^32iOQjxUht$;{9pFom|C|e}wlc`;iKDTU4~;X<& zuy$_>9R5MypLw$P}=v%bjUG(^0;Le}*eIC^qR$}LWj zt;XO<>Q}-DC|3;q#xKkriyWLs^t}n#Y*R7VSuvjI^oO?Wd(5#Jn{*O9Qke)cTy^G& zY7uUW6lQ<)18Kl6`Vzol{P5=t4JHiJs?#b-S42F151Qxx#ymH+Gr5rm#A_*JKkCx# z>M`(-XF@HTqO1SRtiqUKb6-x=^xNj0-pCg?^$)_BK_00M0Mw?U2g+Yu;ZwNDmxyy- zsOk?VXTFo8;Qr39+JU9)AP`A4=;NJV%P{cnPIVmjXxR{C41a`Mf4j{p#U#p$gRnz0 z<0As~dn`}ai@S6bSu3Ukew$2HPX=qHQlU+kbnYSyj{0pOB7X-J*e&l32HeZXtobupLb&4A2!#X3P8ntdNmJ`b8G93ANi} zL&_NO)2|G#vSA0aVPn|Tr=b56gt6Z@WDb(TsYNraC0j9>u~_-@x z=`!;xj9%D@op#g>cbq+Ono^3m!A9~zJD_QwTUDy*p3aI`NoV3^&bH(Y+XoSn3lUU)Y_esQ)KHZw#c0-i=T+U zRJi~P)4IoQBqU2a20i`T!)C3&X3Axog!j31AL|6+*B+#kpW<;R4eP`IDWGp&iYY97 ztFgyWv|(VW+HjM2*s0G)rH+Zex`C~4obkf7;Y4e|3)MlG1SlXmt`G_7(Fv_t5j~s{ zC(CyHI+y^uLYTFrVO`=F;?}INY^(Az{R3m&w$o^4)upJJo5ay6ozau>{qHgH%EjQ3 zReyeKoZs~-(__ErQM`6w7eqJHn_dBIe`E$eI1 z^77F0@{scKQ1bEwzzkUDgdUgnJ#iB$WI#5oIi*Kpm89Lmb$LV+j*+?f5kz1@jee*V zUaUtWPu&)?PPiDO1~_ z7yJd7Gj$EcERx#OvKO^iF>c6?>r|{~?=ta%^aDkj1vOuXQsUs1ikKoa@9D4pemA7J zIv#s}HJUFWx?YI)unO#r8qGxZZ_^UZp~tt8?T#D9X6Z9&)n2gO)a&+95sKNgy6Q2) ze!gPRzhOu2?y>9#8A_*q;XX6b((0M6*1-d~IJ-@m^JrMGsB~r(q+1BB%09Fqe%6Qr za9&Tf^&<7_l=SOLP98r**=~5)#?3rn3%TI-5a~*xox=GOBxoAu(OESK_`u?DD3$%r zJ)yb;wL1i;R^btmXQN+ zm%=+#pmFUojWB;f&RrzzGZ9ZM#_DiD7gtYAP%}N)+l%?9Fj`+}pZ?egfvqCWo+SIf;hsl_c>SDlHr!hH3PEC?!TT1PslCtOy>_29qXCuCB#-eP{7_^eK z-HEXf%l1N650EhcQ&8%8VHLZ!pd|T(OOfmgaeu*cRn9~B%3q3ZK^;wEGl>J3izC!Q zCu}!lHU$41=Ettn6UY+*@c6|ROPLMPYM(9Pv-3!m90G!6S=L{J10VKT#ViKQY~(+h zMdK#YiD!%9lv+&GA6b_MWV&&RIUj6$`%2IWaZ31KoSwVqozBHN zP0d+-7=r$7vf0X;A9#XLeE^k7B==F<)CF1<`Qz>~(RN&XQiwOzt9yNz-Hw@eC248Z zn7ZQQRc+3Ny|T)NabI+gP4d%fSPXTUvpA&RaVWp{o8O)Xq+f#R58W&!aj}sh0NuC(7NycPzypvYR{Bvz(8N&op3z_l60E3UVXtaZ{wmN(svFZD@E6 zg~^2GK7!7#O>X7*H}1dvNp1lunGlSvLiZur_<~ag6Q)M6`=~4Sfo|#fUz0D7DIeZo zefr~d8u#ZdA8*?JegM9{-UNf~UEj$NKphORC!hvDa&gpJ`bpO%E@eLdd;5#s~< zx5;$^w9B}tCkoN`q{7c%eYtxp5U-DajYlkoqQk`AVKQqO_DOsXY>W?>><(~@4>pVs zy4J@~j1Q!k?o8|sEQ}A*j1N|XuUQH|b(rqSKY}n{n=HT8nwh?d*1s1_`NJ4)622=* zTvcx~LXqd$=M7+y6|g%n(odviWb(9U{=;6W{b8>#Bq33frR`XNz@^320wV!ntig_8 z(7^f(qfw-7FW8GgihK#vuDGFFJX-O-NCm1Qm_}|uK9ojW&J(HTgE2Foa__TUysuoY zUH%Vg1s_OrL>UUplCnc2 zYaS2ZilsPjbVu3?bF&GS;~{`^PZ1RMRRrHvY-(pltu^QGWv{L%kQf{po<%;3YHUlK zPY*Y<{CYzERcY1@B@;TPKK2!l!0nTDx5>-G%O%@BCQZ@o1m{{N%Z7VGtJ}|Rf(h78 z8@pAtQGYeRJx$p2vK|=e0VKR*#)hj)>^6=Qj|LSaJ}Lw9h&%^b|foC1D{ZdmxH_5;z1XX zm^kj~tzncz??BHEp%ma0FW#3=*V&BYZYc8`hBD`Psd#Y?x<_!tf3-HkRH2nM9kz*r zmu9Gme(!)PL`sSWR;(HMh7-LZ^c^p44!lxnPXf1uPW4r3w-Z&ol zbG$Jgf5&*y$718t3aBs$6y`clsWJTjKZJ+>lZ-$Oz#1U`OG}+*;F$js^t=Ta;=j7b zcHnHJ|FtGh0FVFAhP%yh4mbqpf9ZS6KNr8_@#~k)BG50o|2KWX(g+uR(znAF3M!aQ zG5|4^kaB`r3KAJoiWG?SXSe6i=5A&b3v$BRmMfV}8sg=aNSk50d`+hcn{#~~yBfXf zR_%>1cQuE3fC?kH>ul;6Jpf0&y?^*6$o^D2rtoPqt2A2GF)GhNesxt zsa%eb(JKYPgP`eShSJE?c!4u;UxspwXE1fo&2JMpS!WvRxO$rl&8-F48S|d9bU{|* zFV7Eiyd22`rg#&avVgz}*onV?YuQkBueTNT$@nqbL5Siwp7y&+un-L#9rW#N%6oi8 zD4U6PXtoidz$Vjn2}F92us|=O-x?Ya0Hs3ZJuHZi5)l+NIumePi=GKXUyuFfsRxV^Lj-Bk7x$iRi}^?Id_aKUMH6fE1C&fx)G#r{z|4cwlN+#} zUPrrfHg&gC>V1;2V-N?RT{!O~f=SFR z%H=vx8a&=r5%Yi{XJ})=8GXd`7K;)Ljp5te-l(q;}f zh1Yc1{w0jR;KeC0bgJ%WT~!y9JHHF8TE+S|i(#2txz`g<%i!FJBO3uD;kUt*VY^;@ zHM(#HL&&7-3w+q*E;P8CmAFVTwjAqx4r5;`YY1O|xWtYY3KSUcL&CqF)2Khm-UyqikLrKDjaTP64lW?%H6S|tRh;gS6A#JPN9SQx7)jU$A zsdAvRAz7UNVB7Lj6Ce&wqw|^YQf*a)v&I1h;!TZs_WO=Jz62CMOya*N^_{}qcw(z? z9s}@rS9^{e|B~`0)cY)p952vL0!XUI(Wjd$>D%LgMsNfKb>vkVUSC7fX-x*aIy%)ASyg!aP&md7v9?6q?AWN0w=jlEw7wLv46-} zIE86_b2N78k3;dfdjsYlxI_bGSw8~M#E$gS*grd1Mxje^>*)CLJ2U|NoL0jy-@55N z;UFIN7EuI`m2^~r_wrgPo5wzkb$@Yu;BL+D>cRH~80;P3K-h#(tcL=rNLaGSYn4P{ zDCk$yt}2P+beBr@SapWZ zqM1&{8-X`N!tFn^3-gkws|KiBA)P&wg{Rv0DP1RLd%)B<*9e zI<#tMI4wX{1s6v}mC%Tyd&f~u9+rx5bTze2+Dgr%ytr%`aSEGwX8?C=ir`5sqLl}V z;5IVY9PdRnb~Lr>Uk#F_NQ8?YqkAV{43svG~wFpF#=&;p#(c zknG4-D5tUP(1o3unXyNqF~VXt%LN23&yOT81Zo)8l9gNdd(bIQ{DU3 zxi!B;DkHjbnw_E|tz-umOuRQykUd>YQ(Mo4h2#fMvdP8kh0noLRu;Q+A?Yn`gXY=3 zrpvBianGgm2ffsz9OX_7GjcddjMGag0L$t`7vrXlyev`DO9e1x;XY1@p%Yx&S(b7k zA9YedwzrKqWYuL>^dm>9)<~U0cT}B8XXI$=oih-DW-%oBI^UbmZ~=vD^1=E5=x~T4 z^p*MD_c3S-(nNaym**pA?~xU6WUd^l7GatMn(nYVl+J{4;07;yzPqxopksa7dJQ^f z$w_?{yV2O51R4M{V9MkHJYJ4iK=xFH(_pby4Qyp>#>oaYDVcdrXpstcUm3Yv3WKf2ea*- zlRl^1EzBkuKTIr`(^0R@)DQ`-AVcOwmrN)IR+@|ZhiA+gTPvmssO+D`HiR*c#Un-f z|D+lpXgV!wqPaG)ta6buZ(orcMUU*NxZ_(KX8~qXW>J>cLK=1iDTT#2FV=)#)i}C- znJ!exaCRl@g%*&KNyrhz`PLpK2VYq4Z5(Fn2~}JQjWp{ZK|^c7+(18UyMiW*)+k&`Nk8ClSp*tIIE+#Az`v+52W1gB$g zyaBM4kA(H_$TQ_e=^s0gMu2gd^zN3LM6z`48(otlz9w5x1hztE{j0k5J1H=LQ+gR$ zZqkmFqK&0|Y^Ir$E{k~cdgnsE6{KXqu#?Nb*q@J1n|ak)@IuOCYjG14SNSM9hyo}n z3o8Ddvn%iOkD-#D#XGk_yT4zwDy%%e;toK`&|uQt`H9d6akm z&hd%zocVJ_Fh91rXOQ5@OAa&5`?o$TCFeNKg4?UmK@|vc@PNJWE)#efK5hRscr9Nx zpU{X`yID4h6b=#07paBMnu_=pBfAAt`#GU9*ZJx-)=aLM0CTGSKBXzj39jyT04(6` zKD$Q{+^;?cZXgqd#9;5j?8(H0Z%}y?;*BYa7(D*4^=Cyppvmr;&=+Abtwz&q=XFN( zT2dKTQg~z;@b2kf%etTgCqX{m67UYlK=#fp|UG@u^vcwh-usl)VF zrSnN^GN?CTrgV2ULr&=IW+l-?NCwm;1^GYLar)$0GkF%*u$;l!!PCQquy?LGtU9ol6J_^DlMt#(BSnHwn}WFpsemj)`v&N)d%=-3tNTQDAng zqJskiZG$TaR+nn(`{FY`QCR@Y0Y-#jJVDLb@t{HF&DqDHjPC-@dICkmCV(>Zq2cZN zK9?tiCk1WZCvTPjWmCDY=^uGE7}sPSzJ6)ywtM285Ko#q7L$rk78JqRPS2>;`M&;;8(>OUqAA&XvFT~qbs>w$ zQYVL=E;!PoNs(Zxn&*jAJ+w7s+cS+JsB zQaV&RSl3IkiDOsE4C`W3;3X;nc}J@*Mu_1t$4B{PebhXd0JsvdP7F&|aq0{Ir7)(a z92F#SNKB^HSZSZyJbJvyHl8ksf`^rQ)fc8%6H8gsrld-jjCZPzlu#`!MpynaUTl3d zL&}l1w&qJHcdB!aA_C;W(sOG*wCPpC?S}Bi7oX}vRR>&e+VtZDYkTZDZ9C#!8

$llX<0RaMtR(~?6h$T$sPl%a{&;Yo)ger}16d97lAc)wG z5-YLVDTSn)Wbnrp=Cf0zWw3bv<`tDpStCVoOmgbd(Rox}u8qp)z#x~P=~gD4DY4HN z5i7HYL|;T*{~7~*wb}YA+^teI_n&&ucdwnDx7kHyWlmi6IDBokZW)U%1}tt!A8dC&LDD|wKf*p z5iCTi(~>@9YByc|)xM!N__v0XU&ViHWNTwUAX6i1?x5W7+%25U>ca;Tl75i5E{kUn z9gBy}e`-o^mkca!=P$x3I%jXhNJSDWr=J56iAqX?WDL0R|DFzC* zAMrsXWPk5L(NlY51NZK@d>YS=ax9N_Eqj^NQ&(Z7FD|QC@;4l+jOAqm$V52xYUzvR zveVVv`3e z7)b6Hmfb%|#ariecT+60%4M2YySK2kz;hX!=#$q2;b|8e|7K;-ol+(!Fs$0TEp{!nNdiq5Z70@3k+2!!NQwTz2# z%{x#caVj5pJD?x4;z)Cwq&izRXmDyk}PO_>!Y`T@=z+LQ_iwO z2mkh?Ua2Zz;FQI1L6YN@DC1L7Rv(doA`h4bHJHz1?#FyQkerpAJzI!tf51g+Q!^7A zNDr=lPy%zE`WCUfM*Z<4G}u{)Dzuh&mDJEbT8OJ4XW+2*bjJI-LGS#6PO}d}5*=D; zf9QMwZ_M;K>~JyMUZp8x5w=!Qumog>}#wfu0$c8Se-98 zNSjBQG^;S_Qz2m~c&beIt?2;++ytqZ(XPkht%K(W&$a{S%?fK|XU5gL1Ac+oy5 zJht8OfIw|c#guNT-ppBsN&N7qul>Q{)CIf=mhXiBcz5je`}|pE4U1Z`G=hM5Elu|= z?g1^4R-f}g)<7b0bL(5GHM9OBQ)YYYk71$Qfi*F^te;e*tIfAMX%VYxYCC{7X+s$` z3J7_XT-T{WQN5#DX?HNZJt<3fRA^dPcc#F4A~0gwx!9!G;&c|~;n?i$9-56{s08J( ze@)5yd&S#ze`nlFcZNbb#;sWO-?)nmMM9Y>C5E$CI&XIWi9G>hcL~P(t+7b!h#-Fd zVZt3Hc+fzvoM(`9x7aL13o4)qCnt5((W_cl7R+2Ia34MJUq8+C1^kRiJFhl#L1aw?-lA>PCt8aNBhbMe&0xGV0j^c`ajb$#}u19WS_wJ1h)$$;)X@+*@@d zVK=)fMxnDXlup86v6kqd6dVtk$kX^m434P4-2o@$CDXL)N)f`#0L{ymjYVM<>!qF| z1s~9c>4u0*vJ_KnRs)pvCl0Z_GU(W}(9U4Rn>GsT)9QqZy?o8w)Z|3mWTyALlG2cd zT0n21!8HgFNz&OoA9d~HLxNEB|m%k$_1!W*Bvc@-C={5;T69+ zuN`0mo6x0Y9qwbIHYeIc6)>j0V}Ba!WP&_)R{S~wrO%Tznko1EF#eTCz(VF+QS(PHz@Naw3eerqd}E7 zbr{1;R}H54&kkTHL2bhSwGZ()U9aCnHg>mznHW=#D*Dc!c1>8A zQHN4iC>P`I_)2fhI=@Bf1TEu#fKR_I6NgH{mk8+m2#UEL{VM63YlFC9+TRG$^_TQt zrCcVmq-k4x3&J_YNtvNwK3e)3N#D9ZRHFAl`keAc>&%sH*-TdSuKs2KTb@&`51Uko zC!%XzB0(IW%77iXkfOgPn;IET(%THrl=065y3Ws{6LQi>%aiNNQULzMyMt?;w^f;9 zAq1S?-NV95d7(8DcNZkV%KJ89yo!OBq!d`{G)QKm`~2S8i}kD@&dS5oHtCMsdwhJy?%9&+*=6n?Ib2CPciD62KjP@l;4e#w z9TpE`({Cha$L}*bXx=)yn>9YS8;>6e@B~o!J<4+vmUn?N933AF&w;lBvAWaM6hGQM zl*WHlZJF4%<+T!R7pbnRzjEk))qL=P+lxHF2J$%ah(9R1>fT)sdzYs7eCQ`x5|jXp zns5p;mekydoF)m7UHHuzJ1lzOLv15_)V@&C1WJ_klIl&TBnQ3*4*R&J(ren4w*b_9 zQhXmVlYq2uqYE$#!68Eh0sWY%A+2|ev~N1VjG^R5i7d?#pip4+BZKgnEbUul46cd9 zBJR0i8qo34@)6U(A^CfDZPo?fN)IE%Nle*PbrjRQf$tQ4^@wir4HzzkCbJOd`UYqD zrr}#Gr|D*VH!|yn%F(ra6IuiO1TY~;52plfhUU4xJ=O+1YlEPj+YrW43pq-pJFapE z_dNjb)wibLM&L-jBVE7@>lFDA{Fm<&s?0U(rkPqy7fa}Ve~wffONGRkFOsGxh3V%n zng&od(zSt4_afrOur8K_gW_Syz?J$44Ou61pp4suD7Z_LifF@kXi3Ot6(Cijz9pF^ zDWXP1wiP-6E=)TkaceZ@T6AHWn(47^;O~4O$%vnB39O%P@wqF#)3VsM`CQxR$%bt0 zRAJ4TaaNS#D~X%%GyL&{?K()Tta%W_zbg)Qia!;qcWh3fGejowwG&Vb0;;0|P^Lam+(8`|ZprgXN9GSA1wbbPS&lPjb@ zQN-pPIqp2=V%*(k=@B)(E#V@vq zJGDyg;5D|?{Ed=Lu*IU-^hX1}Tm8{*c+@hft^q`JC|l(oG8@Uy9d|Z|9F$n)rAM)J z=0xHG0){nfC3C`VA1O#}ydqxPCBBj+gsmkA+K+!G0j7-A^G+t|PF_2yvCAm}WEbUn zSObnTK}XLhhqJn`QFj^_?m14=ydi@0C6Z4phYp>09SM<@Atb7mCh~d?*LI%NNI|dHn4P z!$npe=fCQufLkM1POI$3?ttjt0B6xDzT}HMQxR6uakO z8~ZO&RFB7%IXVznJ>6-dmdt~EEb|ReTfk|CZmqL)N(v5;PQo?zeQ`+yE@ z;SEpo$xP(8vq%UWVQ zqru41p*5Yn&}Ht8xXN$hPv@dAY`q$jdQP4k$#T$^wG^~wpDD~oO!{pi0f6{6cE`Ht zq;|+iKQ~p>*s3X;#>(6X&y^=iXLoAV^qCVZcou&EW@K<#9>et_;thr4qiDsw)SPlPwNhJwZ{qx(5?ql385{jl$(ib*|X*5K?u2tX^0Xx2rf!W&&< zR=rQmqUyEqgna3Z{q=;j)@x1rcL>NUqD4GFu_AYJ>3;D(#y8B()LE{+V!CbhU&y~x z)SF!7bosieDh%gG;zwg}l`gyyN};GZ1=520a9Hd{F{aI^M$pL;-5%M`*ap>w&06RP z?zHL2bBO$Ub~y*DZNRlg-OgFcnHubfM^m5>@sq%*#_F@b56B~St-j2O!j+RxP_Z4? z;(2?gT2HQ!)0xV67GC$F!$)2(;R>AYX6;sZXBPaeXv6qXOJ5KY+XIl9{odJhbJVpY zci^OWhUt7qN>!VZ)V8CyKy9Jgg*b|o{exfamQakiE4Yt!D3ss+yllNuiMm9RlDi zrIJv@Ke99$0YLrx7lV<^JcMVFqWw+eQ3_n*xiFn7p_$>CX5p!J9V%sqs+M0)+EFu9 zT9JUfu1gzN;hYT5H?>ND7_U0=+_3siAP8Sk!VddfJx{}6ZV98~E%D9SdZaxpMbybQ zz9mvq(p^@UIZ{g2C3TYqSgUVwv#CRP(f`M-U{M2!7Z8V0Q9nTmf3K;C-(52uU6Zo3 z((xvKAx47pXuQ^UywukIreZvWNJ3|8BjN!VS<0)rX2g_r>HPEp&w`KMm0i<;EO-Oc z095pb71THM)dft>xl_O0EN8LNH0)gbvhz(nQrghcqybw6k5@%K?A8~&@X4#{BY!#^ zUB&QO1RQKRbiDCAf`FrZCUBxfv+GM}USoS~6dl)cXHF0E$3$zY<$&O#sn+p+fzX&hHjFL}lRWU(&C< z8nlY%ifn3_S|gzT8RK;%)n{D&8?E+CYNg7KRUKyD1Z}f_DIGSYA-$q!{=wCMs{djz zEJj|m@S)CVRhuUe@CB)iYJVL`+7}7i3|HZZNuP7|3u4mf7?Y9^jim?wlfE1?nK{-{ zom0OyHhpQZ>1(cjqk*wL<4p30`|IkkO@)(yCL66uT$9-jYqx%D%&0rb3FT0VmXjn%c)RZYcJ^{eWdO3Nk}dm_cH#Y`cz+QC*EuFwRZ#%Q~?lO^6f*M(37i~ zEsc7AK?;WxlXJE4T8XYr;Mzng0nD?)8mkWmN)TD$(n=YyR%&0ywJF+E1{xZx8uD!m zoMfRUSDUVt>)H&i9jeVl17LNXf}SB+#)CLzmex4GWi?kjj81H&!wR+8T%E5S;gmJ% zp_0HB>>Z@V_^j9EluxudoDI(9?~-t+J+;!+ zEzi*AyXCFgd|j(xFeBT=7?)OsoIiBttiukUZJt9d|Iw{2(vC*nXiFK)&$cK!hKO*T zkhROWQm8FwaDR5S+Szd?y;Nkon*&0=f-6m0ol`CG!G)Y^j+3urawbu?2aXea7g3)fDHo2i5*XrJ3WkvgYG>hn58gg%#?M9TC+Y)JQ^R;_iS|{2I+fBb0@7VyOiaT__ zT-s&^<8AHQ{Xu_wXM1w{h)dhTpx7BnPZR}l9nPKD>U0L9oLdRNkXUD;N1g(IH8~`a zmX5G*z5i5QJ3IYZp%J5^EuoUkxD+oa)3tN5J<`_JUBWT7_D;`buqKmE6Aqb4?%00b zjJ@;o({J}?J7&UeEv&^Cdv_P2f~V<@HYdh@Vp_j!O_Q;1lQCkseMH4`E z9J#Gx7xKXxRp4b?bgptL84E~x^h zw{8M$j429#Xb7ADdm0)&v_QOSXHXI(3)Q6_6Qoa$PLj7W$b-f-5*>Zstu7dS=-uHeo81(jzX}`)4H*|HGmVOzm?A4+8~&f zYqo9hMB7kxXub+tKL?N_%$9nBVb6f_@IytPF`JM(2#T~E&Y0? z2}wg!X>(t9N`zE@1N15YPAq-&P6313zUgUxQf=M)4T8*%yVg3kRLSHCAvT1uh z-yd1*^LPWHgsTJnkx)Rmu)?09`qr&l(bBZCrn(VDvj&-e7d4i3cO3v4^}4T`1Y}H_ z4+;3!)g)mlw-WLNju9u7;`r8kP_UhPMnc-iK(HA)KLo8wyFZ#xuCTcGxi8Y6VR0~% zxLwif{uyZ4ri!2~!@0WFx53l8rJ|MQd6A}$AC{6>iE-~i&7h=pB@kZWhl$EA)FqF7 ze3~@(9e4(R(~Klu#^ivMW(6!!S8FwWgiKQZM%(fR z;|l7tx!VH@DMm1xC@eN7fKo?;-zXYYlS^T?#77msC{vx5C7qAz4V{+}>Ix;H7)%f$ z$9e>_tw&9m5Xr3FP-{;P(D(nke?BBJ-MHyue-a(JJ~DI;+{b zyKGGp`eL25*9dmkieoyJqxWtp7Aex$%MDDNlEZzlElQ185bRYS4&@%y?Lae+x6~8# zimhOMeNO?0p|duteX+fHlK#jWNr=AH=Wt|gfeItm#y_>5EodH$0zU>99C#YV(z^D& z6rA3Fzw9-UdWtN2TnWMe%>@icURt zfKB{n@pz>VN1%1^nqXZh7|-J}Sl71`z4Ii0U642vT6>JEVPlVUaz3n84+gq%P7vNW zH0#%aMQx%d5~0_#b!n@v=QAkrc*Vw+O`!zjW2LsST};yEPHZ_olex*!x;+G4k+!>( zg|w8_GvUvf{PtzvpF>ff^AL8pS7jv-HkuepQcrI*rc`y3D{e4X^O~pV%*dX#liEXn zUjOU^7te+{7!$$yhC5*3v=#XTl5YeZU`7P*ae1_o1%EPfR4`pnF2 zgsM<%uVFNLJdNhIJq4xCj#ujTvb~RsK> zCMGdk7CP*++!Vt2@7(SIAu|Z4}-t=KWA~Dg(q`= zfhS^rIyOyf z^j>_)_l)bQn-T8!lt`8$x&MO|yTwjn%K zkyt-tn7Pnwn}?Hrcp!Z6%>8ZKE5;@9GR8je+1+s}FsST3M7my+zRD$SH`amjO$j_o zG*N+G=iAdfzOuTirlGo}X7Rj#$;(h){K3{hr`H$TCL9B}DOq&Ez~39a$qran-gn{~ zTY^uO-z;G2Q5|YS87PC}Zo-~$Yg?0NL!{T6lTZA8NVphiV^%7mPwZUPnClWPkM;RQ`x1)%)(HtUGBDYt>tjQxCH{umwCR z^^DK=D}d4#KGHiE%hoi1#VZ&DU8$OIRKyoexRdA>9Ax)&pU@(6HhbS5)pdXR5`M== z$%4`(U~EC_ZVd%G+k?iBfz}Q3y>QBTtN%su`+ioswGhCKMT8o$+j`b{&jW%a3dWoL z5UjKoz2}4K860A|I;(qfbVi7gPqn!&=-&i`hJW)$KO{?UslsW0BRl$T2qZTgDQ`-wZ#BR0}OKI5MCA?8`WwkP*x0j$rn zW$EqWH3z{3Ob^w;a7@$5U~g;Fmh~QlEBlcInILJT;S@g|+lau9AaeAm7$8o^Kp`9~?+I_Emu>71$iLucnfXAiSDoRYzX2cLQ-B!$kR-%Hx; zD?FqHu1>3v>BxP@tH2P4q$4D)X=AZjqaNI!Sf-wTcS!GNhQli&8#2iqAJ{NE?YlpTO@%zhiE?97>tYfUFR%8uL{V=mH&RdUJ;(_4x_suA{a*0g zYY>0i&xpQ1aOC0m6)$Z-WjjmJ+Ygrg7Zh240ri+w)hpLB7#Z-iuk(6JQX7{X$zZZ=+kfjw zlZ47|!qwPgQ45O(42C*A)8j}-FgUc^b1xjvJTYS14m$o|R8P$q6qv)>lWHZi8JwS1 z6q0iQeO1P6-=dSjSQ9?y#DB@*eNdZ_byZ$gn8XesVW1m0;LJSDL@JX_Pf8AdWAOHV zUad)C<^Cm!#auZU+MFddX7$15Myc5Kv>n%e!8|LRXEL~>e+#*V*JKl~bQ>R)9Eyg} zZ0isDV;jJg%wSMjn(Pe5Em+dlpsxAo?c&o5(RFGR8^15{)3(%Zv7P*MdJJ8RI$tEZ z!bcMXZAOjy7>z{3o(?0niVV_!T*YqJIM*S%Ydm+AxF#?-B#xLrNKQt1t$>uaKUyyi zyF!0Ax845hCwBX6K4c7FZm3jJ8wzdg?C5#wPIxfcQOx|NF=WrrPENgBaJOTTq<{=`6@{n z%0L~vZS_Q##AtZ^{->e!a%GC>_Cybt3n#=GRk1y*<`%PJf224ViWbu`D8-@m#R=ML z%JN_~8A&yVC^MJ43S5QwYd-y2z+L&Sq4?`4`n8C=2D^s1T$N}JalUrB7UN98T3E|Q z-xil^2?{y=STBApMK^+fwRGTje0mu@y_~xSxCRb(tq^Ar?T>j2N2;fh8n`Rh#Rt1q zitXonbUjc6n&^>L+@-q^yESp8?%q=}`GC)^rDvPDOLi%PT?%^jH3(o>klz>4T_-YF zoP?;uR<&`gXy2(4+OpQXL~Nz)#Tn!~T-9Bk^soFl?&Zy(sN3CtywUC???c95K*P#< zaV$q;Q$`BL$nFhNI=K5EeFp?~W?O^jjf z;ibFKn?F4BR9eCz_3ro!;kpY4nvjt;>aHOA>OD6_u{9dQILHMF^0c$GZ)xAw&eJZ? zF48X1F4Hd8uGFr7)~?mA)2^2$Xg6p#N@u_Ta7))oTLEAMEP@LB%z@F6kDn3@ghAr7 z2=CG78aS5PX*c0-rrk`}*ircV7TfPl_UCQ zJ6_>N58C&&JMfcfcWOJ#-plb0-BZgR0ryU;BTZbq)b_AyJ$x73IXGyTy37 zf)2OYpmSN!xwIc>4~TwU+D`33(b}awq&tZQjg7gty(@4S*q$9V1^58?TaBbZ)NaNj5nYUy&EPV><<|-)j4Qo;O zS3U}>ij=&yU2xnBkXNM0UC>gbpy;jYg4Suz?f~~(b=tE<>fQ&1hE~H;?0YHHicf=E z(^3C_0Ukweb)oh>hOHh)9eEMPz)Q%vm*Ft@G0cUZKoz_S_3)a-(J?SV`=R!v;BAA2 zU4ypQl)MJ*Df}jBPviH&pt!z*xjIOTb-43OL1Dk8&G(p6e>lQDbQ{bdlxv?s0&IvO zyke?A@p*8IFDI{=f~dLbraU`MAT|J|A}@)5pEJd$f!HC44!~Q8*iW&=+b{%vjsU#_ zv4LA%z4HB=)Mxy00 zkjU8$r#uXs%SM(>eH=~)*bQgzfB|)T;QY1IcEN=RHp4+D!elrXwd*!PQ#bzoErR=h zI|Tgq2=a%>hd*GePq5vW*z$91^973Ue=Hu9L5}ted>^$Mp|E&xnD#7cIuqZ1q&5x+(fh<2O!GiludaEF6n|RcUnXix`LBQ zT+(waIC8WXwU>~`@zLqZ%cksW*m^sEY%>S^F%1aU7z0YLA_X}1TQzv*LV31wp2AQ8 zy5QQKbJZesu2!Tz4coTCfFiXEzBd=`b^%@4F0MQU+aG~ju?-G#$6UQge*yHod+`gR zeE&AcEkd!pe+MXRuB*uPJPa#xb-@E&@NnMlE_i$!=nCGGXXjA!y*I!SMS2&1Je~KW zE_nV1C^LS%EPjkOe*8rI7;OA_6@OS=xC?S_09S!~8!RYL2G6_=W)Fqehr*jf;im=4 zg}dQrL*c#M@P6xV`0d8lD|W+&J3yVAi=ynK;u-1&)=^71O=voN!lx@5IURyA8D^@uL*bIW$kqo_Z)ZvF4W$@ zZ*GM6P@xtoiBL=&@tY7w@$F5x0>w{ei?p}2pQ3bYY=QPRepBMYD%z88}}G1kzlSi z?RhAkrifcb%JVRAT9MMl%8Vx-W7E)|)=&kpRSy1~fj`x~bM^EO>3fmHDx3|sqqU>Y zhY-%&;Sup^2;U`Wu7TB{-?t1@Rts*n0t#3i6tQ|(%#MMjY$YsbO;E>H!7*$NG_hl0 z4QochI}Qc!1UQL*odj*n10l8!&SriTz?0z;wh^vi0l0w$;TF7pJ8OkIm>2G5K6rp_ zfQPW%Zfv&~+x-aJy@u`HW*tKCmZFdq!6n+yQ7BZn0|vwd3f{5o+dDCV!f%E^N$@l6 z9qnC#?OWO}v|riLdv5FNqb*R zNTV!5jY6V-{95!kRQnB*$C!R_y7&gWmjyOs5f#E?YkV%+hA{tD`<)5%{dkSYKW?f4 zb6wdqgnmvwJ8Czp*g01b22T+NPYeVFNq~PF0Y4Ab@_YpRJd~{qU>Lhl0Dc&DJPh0d za3ae%3-EE;2iori@5TufB<#6Jpdbqrh6DQs2T}xo3Uh5(Bp~sj_K}IiV?XwHW()2i zHn>NBT5ylF;2vqgouAByL23BFj@SNZfXzx!BFNr0n_#2OEmEdTdxRZ>|JUqh%{!oi zTn*teOe@>XP9&!T9S8Ik=IZDNpm)L6<+BZ4%)brz9@f5g$}SeN&APk<@MiBL{KUVW^6f)F2$ z(dwVHPf^Q9!+b#{B7L((rDlst%_f!T+n=?+Ad_<77(p*Z(5nRGZxA%qMz57Il6?iU z@b8z9W6fIISUXZLs{Peu`njeQ7nV&a-p#@ea!b$USeO@ziTDYeKldW`KSbGm67}zY zDa&Z&+IU`QVN+;fQ>gtPTWuN*G?6vQ@~C~5-b!rB&`LaqQuzWx_##T>3kc;)s9i4~ z5G(Pwm~>)Xp^6xAQi60EXiVG9P9=h?$)Yw45lA!?Zz8cV@?meIk@zX1@eYh&KZ8;1 z=P;4ID^Oa9!;CUfBC1WYP?}_+G|3WwrAd}3jj@T+C^8u%oJA=&QG~R=(|3X5KM-x| zbL~Sg<^~wJhn=xDpPjXfeQPf{xLxerGI;P z-d0g2rbsDL3$-FwVNOBrZgxF?88TkPZH8<>5jPFlz_Ox&MFYCnjrr{61VeVKVaRw~ zo%0dhm~@FiRZ<{d8VrS!2E!#CiX|6JlyYE(lnWIShh@?rXp)8qz|X@_cA{y-2B4d2 z7_ouK7Q={f0^O3lRU#2YW3G~BQ364Hdk(+J;_g52n6J%OkhsNKB$+$WjN>Gd*2FzWvT@W*|0=d z%8)`+5T2<5mSVi7o3Id*2V1ZVHeiWCup}9R2eq#p$?-sXa?HZv4?}Vso;f+R|4LhX zAjhNH*N(tEyfZB@3lNxv2+UD^#2bk{imK=Y%&zpnEJ0w7MqrkIrUfP@w&?`r3Cr4V zH1kJ!Xn-C^WvaDIwV_Ok4H`q4>OqlKB18=c(J?quqh)eqP|G8w#^f(qrUyGwRXU-fQdZfp*Ix~U!KY=kojYyrdr^!ewMT*Txt3`@wq_wglbUnM+ zbKMweo6n`ArAah@((4e3cC<&Wz$Gu*86S+0)i+u>|ai)`oQEzg1Fw_hW#!`e3=Vl^Gr|E9fpPh=M z3D+&gPPME^og%d8R5F#Cnaej!r7kl1WGad4MSAz9Qj3$p?$3(ATzUw}@CdqXdyxc> zqEFj@g=E-;0+Q-qS-Ge^uljzevWeG&gr`0W=)@Av$hBxZq(^^8ZN*GSVrzOKl zS^8Q*I8_jC5{2l6pe3q7+96`APvb4ZRCP`6)1sd#B$blXI4pL848AFSvo zWX=$y${u0wJrBcnvtQG8bdd`XupaE{&hs+4OHs4P^sU9 zk<#0?U}{vN|In=C^Z1QhgRd6=0gN{WR~{_e1L3ugV^j#c*k5sU3!1;hm%j}U<{cax zm0S8H%F_FAi1cgQxZ`c(%D`O6x6fHD_LzfB2w@eS>VMq_35(Lw2e#QzwHTlp3+ncN zC1rhpKG5h;y3NSZv4W}j?8`3pRhPuJ!LYI^)4C*;ti_WznM+ppP0g2Fi4EK-4F<#- z6p99esKN8L`}y|!yQPBr>`52AKQeZIytqF)ae&@59v^G03P2{re&&g1Cs{!5VIQxZ z`nWU=c_A~yx1}%O3h6(vRr(5Um;MWPNMFMPsP9k6 z68uP(;dNPoH)R#xlXdu5cEO+I9Qa&zGhOC_$)};P6ad!;VfdlIZ2UG@d|QKmx-*34 zcR1C`Ms3Brd3rv6yTiKnBkS6m@vIdqu$IFLVvam3(1#j61S>FXxDd>ZFzhQBtLp`e z*p~obp+;L@N9)pT@wJhxxVTlg2sIn59G9593?rZB_Q0Y=EFm+Z4@2)?NUkCQ7fHES zQ?f*MQ9hznv>{uvX)ZA-W_altd~dNRdTgcd%Uh@c1325Wh?7_S%(dg$XnXdcNe#mo{`s{xSi~3+GEwdGHt+X|hY&Wa2#SarA`Ep4X>hXQ`F8 z1CGYuXV`upVb`-!WmCGOvryH}!;VJVu}##t2XUSp168mD&PC-q4bH^R7PuHLgY9q^ zJOj^3CyCEDa6hcWU9m@bck zMfh1IkA)@jXjm$bft7MGw8)2mR~`>xc`BSHm&2LzG&ozH4j0Qa;WBv!TrM99+vQns zmwY(vk&l39|r^; z9+lhKUb%z4B8P?Q-v)y*Aajc)M+MF{zp3Db3Cc;rp}d?HB=zC=Z4c<$pYF!(N+elDY9*h-#?NsKd`fkkbZt^~n0&fZb)0mgUSj&NpP@rRbyXi2 z@n503Te^NHm7`E`imS*a9N4|Wfz2s$nGUSGtjJxI(Ao9((C;;V?G(Sp#vT&ChM2z|#$V$3E_{Bs^?bhcnDC4Tl1k=Fdq^K+PsEn9 z1_`ejRhd?_VncP~QoF2RHW!Jp-PFsK=qQ4G9&q`57${!=1@eV3TD}Oz%NN5m`BGRY zUxvE*9at-04jbevpi{mQ&XTW!3*@U|t9&ioCVv-y9+t0zz4BI^Ca;HgnogAD=uj^%|VCP~3>Uup2_9N2M z`2TtQ{}TTH$!_V@9nerWP0039(@deJu!H1(yi0mBCX;WYO#Xat{CZ3(@zy(d>lb^I zlI;bERE!w2VE__iC(6`bB*H^bBtHzr@*^-=?t(e;E~t=qL$&-E)Zu57{5TvhKM6kh zDQgLDGRzWEqd*bHTd`!%dW%r&t^DSCeTqKSO!gFjax4<%zfg*Qk?UW=h%Yg6w@NL4 zq<5sZkxakRr#U5~q+g~@M#<=&$}fOReld&u>x(@lqom*J(;b2Nb$Vc4M_}GSVBYMj zWU%z1UY;f{l|INIE|q_YTJS5#lYfmi{Wodj(in91oYQk@;!doPeXP$&&buU| z@rTUvF7h7{lTXlX`7>hjC)DXrQH=k8k~Z&>ywaRe-X#g6MAiPYeyEAk%QzYt&haUE z7wJ!gl-5%g#X#yGh}6H(NPU3_{Tuc3b2L(4T1F~nnzSU-6hmsfg_PYi%@o!-2S(Vk zBV!3YGdp6T*ByqR#V|zUWnfsFis4^(z>!vFsiZ8HR5C17>V(`ADMaoag*69%1DJfp z4TXvf!xaU}6cuLS=Nv_Y3MB`YE4en(%(6&RYmuhbB2BG6%Op*$MVeYMg%N40x=9>4 zkVcddN-V2CnwpH^$6f9r>tAUtXEsW_ES z|J-SpLzDJH6Rm_Y3TZzEhAPE>Nc(XXjSFq!S8UO^*m4evE%7V1Xk2X3c$kgGW5`|% zO-jynV>14uAD&*nzQ|0!l*x$L6cnm*L~I%g)pR&SnUPk%VhB3wmxW+=`sILNf_~|< z^&?FE`VJ0FiTDd*7}Dw$VOW6v?|*kd2YJA@+^wrfH@$?MvLf_@yJVJsT2y5|F}f_) zSm>n8gg0RpTe}L>ZQv?!-GJdR5mAp5(e6Bm+7IOjB;p(>Q0kC?^I?Rt0ExH|N|mEv zrcwb_N+r}POQ2af8ctM}f)~G^qAY{cmE~~0Qg4+dmzwEc7e+Kj`ZotofuZ`5$Tk;} zbfaJe@%sXc6&G0ge1XM(iVG}OTwt;SzsZxZ7s`U4DVU-Orj&#B4I-l_X*NsD36C?X zkH1J+b(>?Y8QdAGl1f@LkQK*Tu9Ue;S&bB2gNojaI(96I;#w$Hjj9Q2IDOk`nwXbR*q!XnUQ{snz;b^38esYIpH~!DX z{~Z4h*ews-0ga|RTq)#Ti8;lj`9(^XJS654pR{(DJaljDs&ll}jYm9_Drl4d3iMeh z&_O6tLdcE|n5dir z_2?SQBV#E>xZG5QVPYv>E^<@=`@*_0<6VIuT@7yKO5n;>r~ubk@ddYu{4fi&VHRk^ z4ABzbZnKO0vSg8$$Lb55p&Xqdl-DDa+Yw3>B;^K#^2W4(P{zzt7Eq4Y7n)Fh+k|qc z0p+;ldQ0lSmBd>N+Lb#I#2prBZX0O!YN;3jhgzTwwSk6QUYgA1NwFo6WXLAmmjPrC zAY?lcvIo;bmQ=@;0c2Aw&NhMlElPQEQtnFGjVye;o3|)qt%yC`%A~CLgtm#gU zojf%o$8IlwI(E+>1WzF?o<@8B>^?bm@+`f=1mRt?@OnbBgHWXImdi=3^`7Q$WA=Fo zXP+OVxqA)G-77eMzX~PF>sGz=2$M@R^G>j+HNm3R1Z(DaEG z*86QU?;+$4rOv$FMjSWl)#m*04>J$=k(CFO56?7z4=5j*FV8c>4tW7i5J#EmIC)X` zc|iF{$2_21h0j-8&-Zm6P&d~LoKu0yyJ&!Z0Rxp^BG=x7(aNu2yz)LwQ+@*rmEXY< z<@c~w`4BcJA3>+`2RKXlBV3?-0$Y_&;Wp(j@UZe%*sFX7uPc9pca<;FqypctQi1PS zslbnaEIIhdvLhc^slbn{RNzPY68&f)2OnjU3e=aTO9jeR@&e&}Ez2MkC@;w@6{t#( zqsox4YEY;uFkDsP5LG`wsX)0#U+y@OAANtiiCi53x;hYC>YyxY>T2~BJxt^^dYvOM z%hLmco|`%xfho#7$CU<{2EE>VVfG>F3#e#+!K}zG(u(xma^23kuB5cN@nWVOt`Zom zPJ}#ll2vGuXR?tDr~Rd>7?DQl4cae_A_@H%lZ~SUtl}jv!CDf5QR*aprI5W*dZXS1 zdJL0>1Wfc*_-zuY4bxZK{;qNUD>v!KI`Uv;TF+dajyxzw9?a;gJfptWs^R{&2~2^1 zNP5zmWJ(ctipQiLfdI_~w|XSxs&indI?wi!N*plz0#XdHg2W>3WyzDEzE*EGA-lq~ z0K*Zoal7SJYaG?yg+nF+w+Ja!g@9GS0JRc^sf#VoWPnZ4hg-l6w}2aNc_zd4;DQJh&4sxd@?z zOWu75b*kQKPPUHkpvut<-a%EjBY@w7LF!Ft77oBWsK(3Kx)(3|^z~+huo#SY|NTh| zN%CnsV2$+>YL)X#sPb7c|Nb0*^zYBz8@rnR)l|EE6l<#L4z!PVBOx9~`*;u9$NON6 zdOwt?KY;1#PMEJg2#eK6V3qnPoTxqqZR#E?m=I()-+I(7O?cT7)8H^^2BsgPCVTtpM(CkB;CPcJL)`tV2a0rsrmAG zUGfDqZxks{D&CnZrgXu>`SK;AdTH#GF8Q+OVK{wEo%XnV1(igxSb~}AFzr+EUZmAi z=%hUZlhkLCHqThXg9BOEE)cXJY!~P!n{vHC-)Jt^9i|7Upif|n!Twh;PgK}}#5QIQ z{MC-YU)v+_FCy?SA@DDMBk(^);9u@b;H4i(517E;YTB7iLj|~U9NZ%eaG89A-kxS*TD~rW zg=zH{NYwX`sJ}w(d4Hb@rh~nB8A85E52aa{mT$~#VOsqNG5I5Z+JR3IlTXkN{0aS} z^b6B5jBE?j7DnAIOebTM=sex3cckOY&6#lKpNP-Dku#qoK3^Po&fJlnGq-2P8I3kj zl5qA`amJLPP*b2-(+>h??n0F0o>96pLzD(0N`n!ld_-voqLhay4c#Y{Jo}E4ehPle zl||=F57S@y-t;(s-kl+iBM`??h~rqqaWvvM25~Ij7aYwzRiEK_R|cgY@_m^tO=}Yn z%Sq@FO+gGNBZOseh&FX!NN+cjG&(3G3wrH?(7A<2)YEkha)6BE69Z8cKkSma4F zhAqdT5VpZY?F1;*TF_HJ3H^2tRN-fhwhoTb*28g{-}34UV4&Wq{S;|dYAa4U-lEy@ zmS`Vu(d>ACE027_{g+6CQsZor|P9zpqFqEMWGelb=D- z{Y2kn%v)@yxz_f?)E6Lc+6ypnkNn))eEEf4@=JSvV|nS_^2+gsk}v zxU|dB6kmlF=pNMBYmhzHLYa0Q%+$8ReC>Lu#LuPLHfYecL$h`x#+KV*z4ko_YB$5F z+AVN@4t`#UbfPVJw`t!OTh-o$?0HbT8}?}TTC6OCtKfF{J__M@xL&+1L9BejV&xN- z<#@tK?9exB3j6=!o&!FLD*bPFXXd>rfsjolB!SSA4M^`&g@h(12u8$!s1)G!IDD4+FDUDn3gFcjA4V zwTy>8CJVTp4CLvVl`L1Zw;Gm=E-_hZj}@3wWKR?jZ26S;T!%hKwtQLV{slKz-T&Nw z#Z{J@;#I)th!mS^qs<}C%447?FCd_ZBUHYPRe$S-JU?DdJ>iD z2{>7K3dW-nPez)}!0Q}kCvxt2SfT6^0c$E;z_?=$Sy{liV^V#k;Lb|Hot2FKR;CNh zcNrspN1OZyL3b4GJ~ye42I7br7v@fbmW@-29xYF*v1f|{Y z>Z#yvuYV^8t$#^~`vte*vmy8|)_~K|hiNXD&vsBMZy@8}#3gyR*=LDNe;?W_A0pHD zKo7K!1C@_ptnx9OrF?>n{uC}j`?yT`5>_eu;0C;|SH40O{T4PV-@)T}-JyJc56>z; z!duEuuvhsN_A9@^VdYRDMfp9DsT>tG;vV>^RU;k)dpN{|hC*Q=z=VdFtQFgy zM8jmDrClQq2%jy`l3QoIbJU2@H%uSFio~b+?H3`xKPxP%y+Ti$9B_)L{^P-J^B>B^3C;~GK zY2!aq%+k zQlHpZN=+g*CR0#&&Ve*?E~JyG|MN{pQl`Q?ZUXsyGD>+|G$f^O^qY$!WhbZ#dGDlX zV}27xVf3@|#7i=jWdQ>A2)=u&AV#-Vf&<4`gk<50>4iOL0u z$_0ta#rjcBRlpum>9Io$$(zr#y=3Vizo$Tg@v z*P`~UZm=zTqwOZ?KsH$O$|kYeuT(hRtW4UG#bED&Wl~%2w@O8oTct>y{nUSqnX~bQ z^|q6Bh*vc-em$g+4Uj=@hFo$Bv?jMA*KdP9_%YTJ%%2}E9wQNuXi(lpVZsm0_{a$K0;tVZZOL9 zPXJ6me_*~sV7^9RzG*OE29YZve(tF>pvG_RDLH`4)=vn}&%Sd{87wvg?zyMZ&;;Zd z;!lp>@#_yGIgT_KIm*aYp1=(A1%}EPfGR*KX{gpVX_V*^tmZ3yp?jqtG2XLHFwH#y zoTh?G(FdS^>5xIwAd7~f12Yp+<&$Rq^u||dtipS<1C4o$(-^n+UB1z8%DyzNzE-9 z0Z6Pt5T+-i$P9sOdJ;6JL!k{F1}D%`7)Xah866E3bc|#0VM{UiFo!u-=HSCVLd3=j zqKy@QL>nuJHkK0&@7P*oir9!_*}vY|y?%uEIij7I5PMB{u!W8VK94QU=mSXOO+Z60 zVp}~Ipn5K}ERezZLLQHcF3cj8yY=qYCFJ6(hX zya^1t1RBw$DBpiY>$nV+eL3`?E1*BU97fSAU_4y~)9BT3K3xr$((B=BdLyi%YlYQ+ zYzA|=)ocS(nE@cUv|lNhuvRc(tzg1hvYJO$YpqR+?Cv^KKNu5m@g2!xtj;3F%4&Mh z9JAw$vKB~-SxqcO0_#7!B6dCbJD0Fqc*|sd-a62jOR{1kyIdX{>zklxOv3aSNn@>s z^j47QZJ^V;P}=TBX}d?1oDxO*LfS-s2$@fAFi+gH*L~gX(2*O-O$mX%s)k*zsW}Fq zA3~rXMxY-i)_E+(D^TL@Fo{mXPw@gGRVmm!6|iu&<7E{JakL*p!mI9)y)+Co8p#X^gU z>@F(fNY9kmMs{yuUQae~tS*ls@dj@aOAZo$fF%A9NxbJ6BEp-=E#N#O(tL7jBBJvi z_S+E5+sPf=Z#jX3xyX!&ZmWiiMn%l=l%_4LGHv1LBJ=N-H7wa8-D9VQ?n5K^Kwa#I z5859-j33sxK77>va5H|mg&DkmNKDb$G|MqXXZi)m^gl?EFVXbvLqF#$Wa-z?ihcua z@mfN^g>LkF^mBfIA$T25_d_{70B6#lU@~5((w|{A{S6kPi! zwFJiD^$fKGoUL|(IcgUn97R0SY6{0xAsllB<>opRBHL1k!;r@QEJmG(RS67X{m90cZxUQA56Bm`p-jt-w zkc>jvlq6&}D4Txod-){4@B4AN@5k%;d_B+Wyw2;K^Zw)Bdm;=msr5>!cW(dUg?syd zz2p%1cG}{A^FT8|>iNy~dS-Z`jceY-Q;q%7gJOVDCwPp}?(s9ZBs`N?;`yZMnom`s4xe$>Vf?Zx)`(qZP+& zX_hhu*>mXA?`ETIBg2@Ms@HLNT5xsI{N~TjH$-@@J{LB6Kuj=7TW6&} zl2sw*R4hwSV}7}7W0j%FL4moka?7zajJ2hF@FvNq9^A3^1!Vj4?;Am^hZTbHYO8mY zwe1*;$BGf0s{uW>TRt^@FgB7olkyJktHCKv1YLxiEHlcAB;vr^nH0(2} zqI&)MNQpk2mmWcuO&N|18u=x5iM3D7d1vE^qk%P#Kxd|V^Dj#J;G>3Lzo}MclB2;BXO zR9w>>Vmpm{Hs!|eP10}k1xdH?z$#!F>G!(JW{2=h;4cp%p46^duy_gN9{Cb2#VYAD zefGAEV(vEaihq@3Qewh)6n&hy9PiDwKldioU;lDZ%)6D^_fEDg$5-^J8nXl$SyOp% zE96ux9pW@succKerrzFyE|fRt8%N+xm;H%K3E3!VBRm?lys{GZHXS@E_}a8d!A)l) zvnv5Fdz#>8(woIa@i>*m3-3Cl^=?-IU)LAm;+Kx)y4{M2)s%_TUd!(Vj!IdXd{R-| z&{Skp^DT{3>=T_@z)E7+#96w4x#WVml;MKQ=e0_i`*xO$|6u5vX*>ArqT$%)&F$!_ z=IUrPM>|>3v`|o0oqF4Ytj|h@;{{D`dFG|Xh9*~d)zpMf;W&Ld@Y~Xs=orXZW$m5d z4y(LbP#lJ78LO0^5k|4^fkBgx<-~2ThI_xmk&hCdygPTQPhW^vY~1ieO#dK&1=#iz{oRA3>`arIk)(D<5YNr7M88n%Nj6oU6RK~ z1eEx*8uLLkyouGluZG7dKc}5qEtfNhptSZ*b#TBm1HWmz=AhSj_2bH`%dCP@ms$Jw zc1NIa{GX*k)gNl;+&7o~qQAoV)n(#t3I||j7OOot5;sij#>tB-z*Hm43G=jLRpX2C zsm59GP$+mlPB87!T2MGnz3+K6$6l?JG=lv|mneQ}8rEdR7w0yO`?{_>K!m?OsT?W8 ze>FFMHbA9yF*XrMZVhg<)3p~V~YxkMDZr^5`S@hd} z$I&XFW%$jr*(bng;Fb&Nb-dKNFCS$I{I)RdW{@sh_nwwJdTbTcOVXP>l^Hg>M-Vr? z2Ya`qVz~j8zSsZa1h4GwvoHK8cH#pB056)`}}BR7wWZz=xi9M?A*C zHPYc#UfE14(y)|Tj;&H#r}{&4QBP8JH?O#Hl~Omc)&jY2^6IVIV#cvNoB1?jw?anE z3??Ld7T;&(?;Y_y_NR?~x>%-(e1u=EE*4Hp;cYqBAIL#CUZeWk8p|9$Ez!Bi8(&kD z@(M>rI9}o-Us;xm2Gy7hZKvRUBkH)Vl_ZaEeJ~Aogz&n&iJcw&Iim5a2g7oWBY}bWc zJ;!>N4;YR}l6wso1%y+L60#`dcbf5;UvQ`m^YVy`-4t@=lqTeTG2CA`IkTLQH8;I`DtR;HyNu;#dD~!1BLOm=`l@G0c|k5>VWjWrhY%A% z$;B33zK7#>gK3QW#zd{^?;4+K)8{Z&BUXx=>oMBHptK|)x z&4!P|HaDmrB<{Y>XDi{MPtCgwwA)ys>jT({dPD3&FPec5bXPUW zeD|k!%7_jw(nqQrD9z0ksk-y8Qia|cu;clTv|rYAfs^rWi<{2Rvv2rZ zdSCLW$e=b+G1Xmbfp+(u{#UKRDtGk;6RtWMAEPJRa_^UMi&kW9l9Yy>8RDqIAhIhe z%f&Yxiv+u@>=5O$2$>hy1c7Q+160O3$1l6Z2ug5o3sO+nadW>I=1t8GUU1tqvgb-P zX;A1jY$mhSy^A5KkqV#ldTBW6nd|*M;pyQ#?WS2{^bqHtP4X;j9o)n`?25dU*xsl> zg)N%cSDFGmPW6%)VR!@boA@C<9(TTSinw2V9c3k?ynBzUz2H`h&8K+I&D?Mi94&@0 z3qFTsUfgA01}u!oWm+Me_LT1-F)g$X=4nD#+lwX(D;!%F1d0aWCYzG*J{qm7n)VC| z_L-UCzsQxx=6e+)_rSF%C9Alju z@P1TJ)K+pa$>@zLeXAUI7>P&GPJG=g&LX#sh;DGKWN3YXK_RQ3brYLwFqP03ytr^` zac#ta^9Vt*a|;6j{QgVaK^W#`nrIVz+OQmLF<0sLgv9ln&DNg1*z4@i%v$tjfeCCn zON3S%ZzN#fQj=L&WszT#tkGep5KknI7i=;Wus*ANp_cRefl9c6DD2@am?dj>25i3X zHQvt$zg5Fyl)5LlfS;kbKlNjGg>Zkae~FpQwNHc1ODq6C;JJ7_pR&94wU5a1_f>~~ zY4oLz8C~ZrYC(kKZr53Prv`?Tl(h`D$|{F<;GA4^_s%%%xi%Zj~phd!wC597;ryxiqASe!&cr;SHVty28OXLv@m zPkc2#lwI00Oowk%9*lhPgoD47GxB?e^tgGrY&Nm(6VcSZf^nH8bJuZTZMn&f?&Du@ zIr_)*+oc0L>@l|{dL@PQnKbp8H1s7eaHg_y-h*YlP9@KP6X*eNiN&G&>wVUYe5dL_20B}GEaQX?n_NKYQZ)6Vw4t~&hnjA{yQ_5ilPQ{Uj&oj zo8TKk%&(f`cm0(vJiNB7G&dNg?s`S^jfpfhNH}=ucAk=hAC(cdUm*ye8mU59IJyun(oA7zc(z60IK8CeTt)s9`yKytW<+ z6GRMd=&OEB5{5<{SA!q2lHIwZNR5T z)@926;hFx*Ooy6yL@URQEeSoRr$PRmSq=tuH+WyxU#!}6YEWZ;5#`>1)5RSvUPzNm zN9y4g80Dv_CH-!huvpWw;BxhD4-XdBGr3P!e18a-ac=ZuzITJJh!S{S-^uilGKv|# zk_BCM9(xU+PpQlJq}!1rbtp4JkiMFHe`Un+{**!6lHIgO*jA%I<0MUNm>A7x+5q{m zuC6J6*d3DIXZQKFa*`o5P7-sY{oiv;9!)ah+v-wSmUiatW2<%hbJQ2Pvafy!xtn%z zxR)kbbJf+uJqaPHDf3I1*D*?jIG#R}?d_E2n^^6ya1&=g+FOP;*}#Qatb{4Th1t;F)|}o}>)zI>-d366 zPuVzbJ#oa}7Jm$u2QK#*wfc0^>*-ezQ{SZ#8nPYwy-dn7|E#BS=C*T2aqz30jp?N>7I zYMQOesD74JD2o02?M0cGUym3Rv}V};xN`-Qum%%POc}LKm1w^6TK|D>MLBMTmz=!z zj@==iznS6fCZ(~5Egq>0WzmE{C~j!vt@L%W-Yc5p(@BgY6%JG@&XFY2MorSG`IFaPNwdlKZ0+{nlqVSD9o-P^+43RZ z^bzaaY>Gn2X0=J@am-WfbrE3l@WAraVR@>s)f5uU;`C<|6*PSz)y=R3>U3ry#+Zq) z2E|&qx##>!4`1@p$K45IKL@RlS!qQnra}bnO-D9GG1gupKF`8owV9eCz{m12hF7M$ z5l({u;?eRqtgK{vM7p;WuVBl5J&pN&N(A`;tixZ@4+G3d_jvh**>32w?AV z5tZI%eBpt`fXj!gs-WN6V#60}xOjqMIOw ziPuE5T#uUDB5y_gKgJ7=%Kk33g+u@55Q7ZYN#d()t#Z%PXuYYidRiMhtcoy+*s-y+ zYI_{4)!)Oqt$4B(-ubmJh~vxGoT}hqVrJ#fI`n^E&#s-t(i?4diVGvLEt%z`ny`mG zN}g&=Fzx;rc$GT-dH6whxc28ELE!ByFGeNSPs>E-k6ssN9o|klPOl6wYVZm*DZz&p_;f`me~_H19DPzk5JmuXs&k()&qFhYC-chA9f-p}2$ksI!Y2h&H4T1gNPA?^+fYZE=d^eiYC<~QSggmpdO zLZ5-W;-~tJ7de{=<_C#b;e>c^0)RXfF_}5|S3V;#><}H-MA17#&sBbRU~z|J5j7Gg z+d4kORv0Cwi<@Ry_Dz@CU^LAXsGItk4)uAB$Qbuw*-pYMBU)dUc9h6&*(L^(_4SE zdkN>qP9%b*Bo@x}`=y);rSd~R)?YU!+Hi_P@wXlkgTH)w zON({L#KNtmyt`k0)W*zWrA-Yc-Hnv;HYjc=&!4S!^d)cSmRQu8yhw}B_tnBo*U8OB zqnN?M-OP;F$<1AZ1CI}0URLDfW~kv-K}Kwkpbo#TX#e9gR>;M!evOcKo$&rwA@92S zp)E8{ZWmvb%n`}4SUsnN-ZK`GQV~2}nCSdqAgt8S&ndqgZy2i~Oi5MvCy3`wd?w2~ z+iSlI-+JU>!C5kwzI=pws1pn`H5P3Ac-_KRLVCE8z@#R%y~EVNb!-(J(f#q(uFLVD z^7dE9?XNoPLr#ZM{ma^sQn`OjD@{GHjLb3ZW}(jOw^0l z(6dHyc!05+m6r@Ra{ER^^g(&3z*L2!IFRlE!uwZfAk_mRnLjw^j`~LCssTc%qr#DI z)PP9zje$BKj=qUi2b56fBqMv&0YUW5jyfQVz7f{|RMDJJ4Zr|x>C*rl&^LUVfEh99 zWo?JC+C0w+fsC3WlQmDl@0tKTG?+UBk;aY`)&iJWz?QAUEivh900=}34+2qvoLxnT zXCi23N&}QQBUXfz#8?jlN&(w<`91xT06m?~pGbD2Q_ZPn;j z9HlVmkcxVz0ek6zNXTOpk`;l0mGST+EaO`p^_lB86k+4(KLiq_o<2Z^4#iO)1Z6(` z1M;7FuAo4oFDNj>?=ue?z(XZMeLQL@2zkW-poESupyL z^8b?W4nc~T0Q?!%X~akwqtlKV8iBFnFrpD8@;_@8%4&IPZKB7ZMB*Et4hpR?2z4-@ zNcnxHL$+B1wCMO)R09=>h#`;-UI;|>Tzq2}PV^<&Flf+&i00dw2+X|)dtHkSo)CVH zuPS=N7q$m5!GVV%p9y0yp5;6JA{aul#3{!PU_cIoV~5sE5Ag_-!-PO+2p|yGbDDpo zQTzZj3k84;qc5q`cL)Rq*-&XwI8kLWz#u{%nw<8Tz!YqdT=5^s$rNBhOC^f%Rj6V? zAc2GsNc>+Z2w3Y=+JE`PNFfV=6uD{&=%Af$4f5`S=ky*%A~9?Mb#&$7 zq9%t{+`c#xVF^${Gaj7ifs=y-Icf=Tp_@9k1e+SnM?vyPrH2@&s|9Ld@Ua3Rn#X7_ zaK@1$wRKL*c?N}WOq}RdL7@alA!~pW9h&d)=TLWWWsCyL4P2c5UPXv+)2PryPL~8+ zbVZ{>n6?JhdS+2-nn(p3fB|i_wE?ZX^CxWx+W{2FhjSRLXNg}y;qMktaM4Qu2inW# z5@*z!prpu#OMn16vUOV7wmBMb`kO-_@_*w()zAP0S>Oz?XVl5#Vi~Cf1$rP= zbWUg6b-5kUZLo+9aUc*d*?+Gh1Qy+2Cvc^sL2B3nlIRd3ZNU)Y82_S3FhQhe^Rcnu z28S>>cXV+f5YxYUs4z^$&V0ZQOQ55+Pm1BN2J@*625^4i`XzNn|8ESikbI2LlhqdO z4YxM?b`jisF0eo#YUdhjkvsDS7XS`4J#(N6^MVWn0(|-ZbU)31=ym}4SuOP0gWcVP zpTS_(snOkKM%CYh2xmhRxbW(NeK=qG#mIbTfCH6uB-mR9TNQ_SXOGJ-Dg(JX13OaC1rSDu=i~xf11nFgj;;VUvdraloyxLNmm~*Q zll!y~i0nDpmVf!jE~oiqb_J!ln*K^Lq8o@pStZ*~tcvAOLgZW5(!XM8Ts($0T;$=m@!bh>JCnPi|Kw+8$> zpIPSazkD_%)8*4VWRpdF_yg*`0QtJ-^kZKA(2E`zbJ_MG5;m%*k{ z+qsj=_Pjeds5Rhd37wO2={up$T?S~;0*5F8sy8QA1rKoMpaiml6U(^4I5z($mMcRi zRPc$A8#(9!rV#ZQ_|XGYfxJ6`m^=Y~wAH{9w1Qs^K$k*_C%}TX)}yTDizn9C{ZM)& zk=LmVgBJ(|t(`!2UVs!jn4gNnX>-Bgc0Y;%5jfYIqxBOi*c&V)%X=vH{|p+64kiM9 zk)6JuY}(vzND^;=1wB-qcW_#GKos21Aku$Xs71`i4Qb>Js1jR4tRYW9c*+f&_>lht D2C|&x delta 66159 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 U06eNbc&AkYlSHM#lL+Gf00fibEdT%j From c6d099c91ce2a7f55cd7b17f64a97ea75ba21f32 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 4 Oct 2024 12:48:51 +0530 Subject: [PATCH 24/45] fix: changelog (#230) --- CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dd709e9..d8f45ac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,57 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Compatible with plugin interface version 6.3 - Adds support for OAuthStorage +### Migration + +```sql +CREATE TABLE IF NOT EXISTS oauth_clients ( + app_id VARCHAR(64), + client_id VARCHAR(128) NOT NULL, + is_client_credentials_only BOOLEAN NOT NULL, + PRIMARY KEY (app_id, client_id), + FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS oauth_revoke ( + app_id VARCHAR(64) DEFAULT 'public', + target_type VARCHAR(16) NOT NULL, + target_value VARCHAR(128) NOT NULL, + timestamp BIGINT NOT NULL, + exp BIGINT NOT NULL, + PRIMARY KEY (app_id, target_type, target_value), + FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS oauth_revoke_timestamp_index ON oauth_revoke(timestamp DESC, app_id DESC); +CREATE INDEX IF NOT EXISTS oauth_revoke_exp_index ON oauth_revoke(exp DESC); + +CREATE TABLE IF NOT EXISTS oauth_m2m_tokens ( + app_id VARCHAR(64) DEFAULT 'public', + client_id VARCHAR(128) NOT NULL, + iat BIGINT NOT NULL, + exp BIGINT NOT NULL, + PRIMARY KEY (app_id, client_id, iat), + FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS oauth_m2m_token_iat_index ON oauth_m2m_tokens(iat DESC, app_id DESC); +CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON oauth_m2m_tokens(exp DESC); + +CREATE TABLE IF NOT EXISTS oauth_logout_challenges ( + app_id VARCHAR(64) DEFAULT 'public', + challenge VARCHAR(128) NOT NULL, + client_id VARCHAR(128) NOT NULL, + post_logout_redirect_uri VARCHAR(1024), + session_handle VARCHAR(128), + state VARCHAR(128), + time_created BIGINT NOT NULL, + PRIMARY KEY (app_id, challenge), + FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS oauth_logout_challenges_time_created_index ON oauth_logout_challenges(time_created DESC); +``` + ## [7.1.3] - 2024-09-04 - Adds index on `last_active_time` for `user_last_active` table to improve the performance of MAU computation. From cff42cbdb5edbef087d819de8e6ffe5905e92dcb Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 4 Oct 2024 12:52:02 +0530 Subject: [PATCH 25/45] adding dev-v7.2.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.2.0.jar | Bin 232868 -> 232868 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.2.0.jar b/jar/postgresql-plugin-7.2.0.jar index 4a35f089cc4c30bec0159c89cc2965f994b84504..86ad2a0a500f6426012719724c6cf997df888727 100644 GIT binary patch delta 1594 zcmY+DZA?>F7{@v1sR+{55M~oMkVWT)aYO`h5MJUo224kXncEaiWEcviycWu9DW$E@ z@*-5`t_vaPgk_USgpQTP4`#+8IyM|JnwU6c8|uV;m|2jym+bz}`Lvtd`};pH=RD7O z?!80(x}koZHg^|kRs{uxgakQ^KJANKeJ1YO=rCsN3sNtRhkB8En<} zQGND>u^lS>IqY){%)eG-!ORFwTMxQo(nEFF)b^g{*->@3gis}JoW&?-XSq&EHBq=< zA3sS*y_z_?0z8?L$5i(HlbQ`PD{b{V;NGJ%DByoV$%z21r{5OKuAG?^e)o8_u>YLc zxe4*tZ_f#P?a!A;bdYh~ao4Jz>z1ak^>mz)-$1MVvG-1KB+vc2jGF$L#i6HFPFUDr zq)H_siW$UW9=Nkxt1eE`^yLP}9DNv~^j6WDz!y>P5G=`@BT1)|g0#Z?ds+3XjbJ@Bo8wTD6M$z#jHW5{Q(2#?RqhO`L1 zT+b}Q5F>W>XdT4h0817XZk}M-;qW{^&cZfAY`wuQiO`dq?79$hKe7TLw*SP2g}AlA zM)gSU{+nf^!HF9@X)PS#&v3g)@kQ~OHSiov;Dbo{GKo)#$aD%nE6&iE$(_ka`L~6S zg+h#Z`AcFrw>$Y6k$j|wKfs&=jXquskNUZ6QwChLQi(ps#pz;?@dhD2J;`&#qQ^tr zDMs7$87J(gI7WCTc9|aGCXt&k$)8$@$gAi1n~3zD=aZt)&aZiq5dI6iR*20Pd4Ui; z-|#{qmZo@}=(FN79}{Bz4Bsni+h+L@k)!>AhX{}U8XpiNo><^l#2ac|7}7^cK_m;KO1n6GRs9W`_a9IhcN7-^+(@T!&UU~Vma0N$;o4$x9ZZg9DdJ^@eEQw^wZAP0E7LG~5g zDCc5rq$cenTo;t&2$`e+NlU!w97e^w#aiWw#an{ z9FpHTWWCf@+24g$Ij%G-Z;$yqqPB!?nJaWIUJX8yNt5@2mytH4nKkSfYOdT>j-yvVm e?oQdxM5p|Wp6j9pEbs54ec)0TrGwkMsr)}_G&i&W delta 1594 zcmYL|eN5F=7{@v1xgZE#$H==`AWgGy#zYW<@iHp{(jZ(~h;9;&3zv5;TwX61xEFax zxRU*BjG+mwB$;rdOY;wxGeo(CF{hiGDHUpBf8-jHy{z*+=a24of1mI3a-Qco=XZX? z1Nz|seL%);(yjLL^7ZxdWI6+0=HeN>d(&)Y(ta=T<9LW4iNC3ID|!8&KAY(r3FqQ@ zJz6a4_h&nW;Ywp)=mh)!@=k%7!ux20Jna4aj*e_{mL9i`n-X+*q&rV5y%S45= z|D4>l8U8o#%u9RS&zDI~kaVNzo<;2IwywYVOoWzQOKV)=_fK;q&;PoDo-WVQ@Ux=$ zq?ru`3tI~cVd-))ci-I;AQs2y`ZIjOjy>|#`pRjo`^yk!KmoTWlm%f7_rgY2sE0_8 zWN%>r_s7w!ObY*4b{;v}Oaj}7NN6Ivv>IYDmHpxkF`LfX{2}&cu@&h_&t|VlkF$^s z2O#un4Kw>djM~`a^$$;^WOxq?Iyb{Lr-t98&b^w$a19E@e>=7 z;`SmNGa$L=Z)5Xuqnd@9;{BjH=ovW>JAbQ&oR{N6}!ARd%Q>97yyQ8swM zLBZNW9>u~4&Zk1KBcDD%@}qnzmPsZufzu`t7yhUMdJpU>paS@x6sRF;3aJeC!$Rsn z+)_k6pstwgV0*EM3qCETHqcR`*6B)Bes3u?z^^Z(BGj>#iTc8RP(}yAKuw1)xMS*)w+XCir+P2jzf<7Ioh3 zEuxP&&)-|f41ews+6$gPLPl_7D;)w`S}6;>+p7A#(?;^LU1=jL{3-3K-q-C^g?igM z)cZc&K?j8WQKxES>Qv!{PVss~+MX^kj^JdMx<)T_Q!VU)ZrYEs%iWX+?&zVC{{WDB BVx#~7 From 41107c5e256b7d2c5f6a2458d63a6c290f0077f0 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Sun, 27 Oct 2024 00:33:21 +0200 Subject: [PATCH 26/45] feat: oauth2 tables and queries --- .../supertokens/storage/postgresql/Start.java | 282 +++++++++- .../postgresql/config/PostgreSQLConfig.java | 22 +- .../postgresql/queries/GeneralQueries.java | 37 +- .../postgresql/queries/OAuthQueries.java | 483 ++++++++++++++++++ 4 files changed, 814 insertions(+), 10 deletions(-) create mode 100644 src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index aee997d4..310e8158 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -48,12 +48,20 @@ import io.supertokens.pluginInterface.jwt.JWTSigningKeyInfo; import io.supertokens.pluginInterface.jwt.exceptions.DuplicateKeyIdException; import io.supertokens.pluginInterface.jwt.sqlstorage.JWTRecipeSQLStorage; -import io.supertokens.pluginInterface.multitenancy.*; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.MultitenancyStorage; +import io.supertokens.pluginInterface.multitenancy.TenantConfig; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateClientTypeException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.multitenancy.sqlStorage.MultitenancySQLStorage; +import io.supertokens.pluginInterface.oauth.OAuthClient; +import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge; +import io.supertokens.pluginInterface.oauth.OAuthStorage; +import io.supertokens.pluginInterface.oauth.exception.DuplicateOAuthLogoutChallengeException; +import io.supertokens.pluginInterface.oauth.exception.OAuthClientNotFoundException; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; import io.supertokens.pluginInterface.passwordless.exception.*; @@ -98,7 +106,10 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLTransactionRollbackException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; @@ -106,7 +117,7 @@ public class Start implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage, JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage, UserIdMappingSQLStorage, MultitenancyStorage, MultitenancySQLStorage, DashboardSQLStorage, TOTPSQLStorage, - ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage { + ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage, OAuthStorage { // these configs are protected from being modified / viewed by the dev using the SuperTokens // SaaS. If the core is not running in SuperTokens SaaS, this array has no effect. @@ -3101,4 +3112,269 @@ public int getDbActivityCount(String dbname) throws SQLException, StorageQueryEx return -1; }); } + + @Override + public OAuthClient getOAuthClientById(AppIdentifier appIdentifier, String clientId) + throws StorageQueryException, OAuthClientNotFoundException { + try { + OAuthClient client = OAuthQueries.getOAuthClientById(this, clientId, appIdentifier); + if (client == null) { + throw new OAuthClientNotFoundException(); + } + return client; + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void addOrUpdateOauthClient(AppIdentifier appIdentifier, String clientId, String clientSecret, boolean isClientCredentialsOnly, boolean enableRefreshTokenRotation) + throws StorageQueryException, TenantOrAppNotFoundException { + try { + OAuthQueries.addOrUpdateOauthClient(this, appIdentifier, clientId, clientSecret, isClientCredentialsOnly, enableRefreshTokenRotation); + } catch (SQLException e) { + ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage(); + PostgreSQLConfig config = Config.getConfig(this); + + if (isForeignKeyConstraintError( + errorMessage, + config.getOAuthClientsTable(), + "app_id")) { + throw new TenantOrAppNotFoundException(appIdentifier); + } + throw new StorageQueryException(e); + } + } + + @Override + public boolean deleteOAuthClient(AppIdentifier appIdentifier, String clientId) throws StorageQueryException { + try { + return OAuthQueries.deleteOAuthClient(this, clientId, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public List getOAuthClients(AppIdentifier appIdentifier, List clientIds) throws StorageQueryException { + try { + return OAuthQueries.getOAuthClients(this, appIdentifier, clientIds); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean revokeOAuthTokenByGID(AppIdentifier appIdentifier, String gid) throws StorageQueryException { + try { + return OAuthQueries.deleteOAuthSessionByGID(this, appIdentifier, gid); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean revokeOAuthTokenByClientId(AppIdentifier appIdentifier, String clientId) + throws StorageQueryException { + try { + return OAuthQueries.deleteOAuthSessionByClientId(this, appIdentifier, clientId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean revokeOAuthTokenByJTI(AppIdentifier appIdentifier, String gid, String jti) + throws StorageQueryException { + try { + return OAuthQueries.deleteJTIFromOAuthSession(this, appIdentifier, gid, jti); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean revokeOAuthTokenBySessionHandle(AppIdentifier appIdentifier, String sessionHandle) + throws StorageQueryException { + try { + return OAuthQueries.deleteOAuthSessionBySessionHandle(this, appIdentifier, sessionHandle); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void addOAuthM2MTokenForStats(AppIdentifier appIdentifier, String clientId, long iat, long exp) + throws StorageQueryException, OAuthClientNotFoundException { + try { + OAuthQueries.addOAuthM2MTokenForStats(this, appIdentifier, clientId, iat, exp); + } catch (SQLException e) { + ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage(); + PostgreSQLConfig config = Config.getConfig(this); + + if (isForeignKeyConstraintError( + errorMessage, + config.getOAuthM2MTokensTable(), + "client_id")) { + throw new OAuthClientNotFoundException(); + } + throw new StorageQueryException(e); + } + } + + @Override + public void deleteExpiredOAuthM2MTokens(long exp) throws StorageQueryException { + try { + OAuthQueries.deleteExpiredOAuthM2MTokens(this, exp); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void addOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge, String clientId, + String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) + throws StorageQueryException, DuplicateOAuthLogoutChallengeException, OAuthClientNotFoundException { + try { + OAuthQueries.addOAuthLogoutChallenge(this, appIdentifier, challenge, clientId, postLogoutRedirectionUri, sessionHandle, state, timeCreated); + } catch (SQLException e) { + ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage(); + PostgreSQLConfig config = Config.getConfig(this); + + if (isPrimaryKeyError(errorMessage, config.getOAuthLogoutChallengesTable())) { + throw new DuplicateOAuthLogoutChallengeException(); + } else if (isForeignKeyConstraintError( + errorMessage, + config.getOAuthLogoutChallengesTable(), + "client_id")) { + throw new OAuthClientNotFoundException(); + } + throw new StorageQueryException(e); + } + } + + @Override + public OAuthLogoutChallenge getOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException { + try { + return OAuthQueries.getOAuthLogoutChallenge(this, appIdentifier, challenge); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void deleteOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException { + try { + OAuthQueries.deleteOAuthLogoutChallenge(this, appIdentifier, challenge); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void deleteOAuthLogoutChallengesBefore(long time) throws StorageQueryException { + try { + OAuthQueries.deleteOAuthLogoutChallengesBefore(this, time); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void createOrUpdateOAuthSession(AppIdentifier appIdentifier, String gid, String clientId, + String externalRefreshToken, String internalRefreshToken, + String sessionHandle, List jtis, long exp) + throws StorageQueryException, OAuthClientNotFoundException { + try { + OAuthQueries.createOrUpdateOAuthSession(this, appIdentifier, gid, clientId, externalRefreshToken, + internalRefreshToken, sessionHandle, jtis, exp); + } catch (SQLException e) { + ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage(); + PostgreSQLConfig config = Config.getConfig(this); + + if (isForeignKeyConstraintError( + errorMessage, + config.getOAuthSessionsTable(), + "client_id")) { + throw new OAuthClientNotFoundException(); + } + throw new StorageQueryException(e); + } + } + + @Override + public String getRefreshTokenMapping(AppIdentifier appIdentifier, String externalRefreshToken) + throws StorageQueryException { + try { + return OAuthQueries.getRefreshTokenMapping(this, appIdentifier, externalRefreshToken); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void deleteExpiredOAuthSessions(long exp) throws StorageQueryException { + try { + OAuthQueries.deleteExpiredOAuthSessions(this, exp); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, false); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfClientCredentialsOnlyOAuthClients(AppIdentifier appIdentifier) + throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, true); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfOAuthM2MTokensCreatedSince(AppIdentifier appIdentifier, long since) + throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfOAuthM2MTokensCreatedSince(this, appIdentifier, since); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countTotalNumberOfOAuthM2MTokensAlive(AppIdentifier appIdentifier) throws StorageQueryException { + try { + return OAuthQueries.countTotalNumberOfOAuthM2MTokensAlive(this, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean isOAuthTokenRevokedByGID(AppIdentifier appIdentifier, String gid) throws StorageQueryException { + try { + return !OAuthQueries.isOAuthSessionExistsByGID(this, appIdentifier, gid); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean isOAuthTokenRevokedByJTI(AppIdentifier appIdentifier, String gid, String jti) + throws StorageQueryException { + try { + return !OAuthQueries.isOAuthSessionExistsByJTI(this, appIdentifier, gid, jti); + } catch (SQLException e) { + throw new StorageQueryException(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 a1ff555c..2a3f47bc 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -23,7 +23,6 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; - import io.supertokens.pluginInterface.ConfigFieldInfo; import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.storage.postgresql.Start; @@ -31,11 +30,7 @@ import java.lang.reflect.Field; import java.net.URI; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; @JsonIgnoreProperties(ignoreUnknown = true) public class PostgreSQLConfig { @@ -450,6 +445,21 @@ public String getTotpUserDevicesTable() { public String getTotpUsedCodesTable() { return addSchemaAndPrefixToTableName("totp_used_codes"); } + public String getOAuthClientsTable() { + return addSchemaAndPrefixToTableName("oauth_clients"); + } + + public String getOAuthM2MTokensTable() { + return addSchemaAndPrefixToTableName("oauth_m2m_tokens"); + } + + public String getOAuthSessionsTable() { + return addSchemaAndPrefixToTableName("oauth_sessions"); + } + + public String getOAuthLogoutChallengesTable() { + return addSchemaAndPrefixToTableName("oauth_logout_challenges"); + } private String addSchemaAndPrefixToTableName(String tableName) { return addSchemaToTableName(postgresql_table_names_prefix + tableName); 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 ac922852..2cc5e554 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -554,6 +554,37 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S update(con, TOTPQueries.getQueryToCreateTenantIdIndexForUsedCodesTable(start), NO_OP_SETTER); } + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthClientsTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(con, OAuthQueries.getQueryToCreateOAuthClientTable(start), NO_OP_SETTER); + } + + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthSessionsTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(con, OAuthQueries.getQueryToCreateOAuthSessionsTable(start), NO_OP_SETTER); + + // index + update(con, OAuthQueries.getQueryToCreateOAuthSessionsExpIndex(start), NO_OP_SETTER); + update(con, OAuthQueries.getQueryToCreateOAuthSessionsExternalRefreshTokenIndex(start), NO_OP_SETTER); + } + + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthM2MTokensTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(con, OAuthQueries.getQueryToCreateOAuthM2MTokensTable(start), NO_OP_SETTER); + + // index + update(con, OAuthQueries.getQueryToCreateOAuthM2MTokenIatIndex(start), NO_OP_SETTER); + update(con, OAuthQueries.getQueryToCreateOAuthM2MTokenExpIndex(start), NO_OP_SETTER); + } + + if (!doesTableExists(start, con, Config.getConfig(start).getOAuthLogoutChallengesTable())) { + getInstance(start).addState(CREATING_NEW_TABLE, null); + update(con, OAuthQueries.getQueryToCreateOAuthLogoutChallengesTable(start), NO_OP_SETTER); + + // index + update(con, OAuthQueries.getQueryToCreateOAuthLogoutChallengesTimeCreatedIndex(start), NO_OP_SETTER); + } + } catch (Exception e) { if (e.getMessage().contains("schema") && e.getMessage().contains("does not exist") && numberOfRetries < 1) { @@ -626,7 +657,11 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer + getConfig(start).getDashboardSessionsTable() + "," + getConfig(start).getTotpUsedCodesTable() + "," + getConfig(start).getTotpUserDevicesTable() + "," - + getConfig(start).getTotpUsersTable(); + + getConfig(start).getTotpUsersTable() + "," + + getConfig(start).getOAuthClientsTable() + "," + + getConfig(start).getOAuthSessionsTable() + "," + + getConfig(start).getOAuthLogoutChallengesTable() + "," + + getConfig(start).getOAuthM2MTokensTable(); update(start, DROP_QUERY, NO_OP_SETTER); } } diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java new file mode 100644 index 00000000..58a26efd --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java @@ -0,0 +1,483 @@ +/* + * 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.queries; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.oauth.OAuthClient; +import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import io.supertokens.storage.postgresql.utils.Utils; +import org.jetbrains.annotations.NotNull; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; + + +public class OAuthQueries { + + public static String getQueryToCreateOAuthClientTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuth2ClientTable = Config.getConfig(start).getOAuthClientsTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuth2ClientTable + " (" + + "app_id VARCHAR(64)," + + "client_id VARCHAR(255) NOT NULL," + + "client_secret TEXT," + + "enable_refresh_token_rotation BOOLEAN NOT NULL," + + "is_client_credentials_only BOOLEAN NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2ClientTable, null, "pkey") + + " PRIMARY KEY (app_id, client_id)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2ClientTable, "app_id", "fkey") + + " FOREIGN KEY(app_id) REFERENCES " + Config.getConfig(start).getAppsTable() + "(app_id) ON DELETE CASCADE " + + ");"; + // @formatter:on + } + + public static String getQueryToCreateOAuthSessionsTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuthSessionsTable = Config.getConfig(start).getOAuthSessionsTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuthSessionsTable + " (" + + "gid VARCHAR(255)," // needed for instrospect. It's much easier to find these records if we have a gid + + "app_id VARCHAR(64) DEFAULT 'public'," + + "client_id VARCHAR(255) NOT NULL," + + "session_handle VARCHAR(128)," + + "external_refresh_token VARCHAR(255) UNIQUE," + + "internal_refresh_token VARCHAR(255) UNIQUE," + + "jti TEXT NOT NULL," // comma separated jti list + + "exp BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthSessionsTable, null, "pkey") + + " PRIMARY KEY (gid)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthSessionsTable, "client_id", "fkey") + + " FOREIGN KEY(app_id, client_id) REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE);"; + // @formatter:on + } + + public static String getQueryToCreateOAuthSessionsExpIndex(Start start) { + String oAuth2SessionTable = Config.getConfig(start).getOAuthSessionsTable(); + return "CREATE INDEX IF NOT EXISTS oauth_session_exp_index ON " + + oAuth2SessionTable + "(exp DESC);"; + } + + public static String getQueryToCreateOAuthSessionsExternalRefreshTokenIndex(Start start) { + String oAuth2SessionTable = Config.getConfig(start).getOAuthSessionsTable(); + return "CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON " + + oAuth2SessionTable + "(app_id, external_refresh_token DESC);"; + } + + public static String getQueryToCreateOAuthM2MTokensTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuth2M2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuth2M2MTokensTable + " (" + + "app_id VARCHAR(64) DEFAULT 'public'," + + "client_id VARCHAR(255) NOT NULL," + + "iat BIGINT NOT NULL," + + "exp BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2M2MTokensTable, null, "pkey") + + " PRIMARY KEY (app_id, client_id, iat)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2M2MTokensTable, "client_id", "fkey") + + " FOREIGN KEY(app_id, client_id)" + + " REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE" + + ");"; + // @formatter:on + } + + public static String getQueryToCreateOAuthM2MTokenIatIndex(Start start) { + String oAuth2M2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable(); + return "CREATE INDEX IF NOT EXISTS oauth_m2m_token_iat_index ON " + + oAuth2M2MTokensTable + "(iat DESC, app_id DESC);"; + } + + public static String getQueryToCreateOAuthM2MTokenExpIndex(Start start) { + String oAuth2M2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable(); + return "CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON " + + oAuth2M2MTokensTable + "(exp DESC);"; + } + + public static String getQueryToCreateOAuthLogoutChallengesTable(Start start) { + String schema = Config.getConfig(start).getTableSchema(); + String oAuth2LogoutChallengesTable = Config.getConfig(start).getOAuthLogoutChallengesTable(); + // @formatter:off + return "CREATE TABLE IF NOT EXISTS " + oAuth2LogoutChallengesTable + " (" + + "app_id VARCHAR(64) DEFAULT 'public'," + + "challenge VARCHAR(128) NOT NULL," + + "client_id VARCHAR(255) NOT NULL," + + "post_logout_redirect_uri VARCHAR(1024)," + + "session_handle VARCHAR(128)," + + "state VARCHAR(128)," + + "time_created BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2LogoutChallengesTable, null, "pkey") + + " PRIMARY KEY (app_id, challenge)," + + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2LogoutChallengesTable, "client_id", "fkey") + + " FOREIGN KEY(app_id, client_id)" + + " REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE" + + ");"; + // @formatter:on + } + + public static String getQueryToCreateOAuthLogoutChallengesTimeCreatedIndex(Start start) { + String oAuth2LogoutChallengesTable = Config.getConfig(start).getOAuthLogoutChallengesTable(); + return "CREATE INDEX IF NOT EXISTS oauth_logout_challenges_time_created_index ON " + + oAuth2LogoutChallengesTable + "(time_created DESC);"; + } + + public static OAuthClient getOAuthClientById(Start start, String clientId, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String QUERY = "SELECT client_secret, is_client_credentials_only, enable_refresh_token_rotation FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE client_id = ? AND app_id = ?"; + + return execute(start, QUERY, pst -> { + pst.setString(1, clientId); + pst.setString(2, appIdentifier.getAppId()); + }, (result) -> { + if (result.next()) { + return new OAuthClient(clientId, result.getString("client_secret"), result.getBoolean("is_client_credentials_only"), result.getBoolean("enable_refresh_token_rotation")); + } + return null; + }); + } + + public static void createOrUpdateOAuthSession(Start start, AppIdentifier appIdentifier, @NotNull String gid, @NotNull String clientId, + String externalRefreshToken, String internalRefreshToken, String sessionHandle, + List jtis, long exp) + throws SQLException, StorageQueryException { + String sessionTable = Config.getConfig(start).getOAuthSessionsTable(); + String QUERY = "INSERT INTO " + sessionTable + + " (gid, client_id, app_id, external_refresh_token, internal_refresh_token, session_handle, jti, exp) VALUES (?, ?, ?, ?, ?, ?, ?, ?) " + + "ON CONFLICT (gid) DO UPDATE SET external_refresh_token = ?, internal_refresh_token = ?, " + + "session_handle = ? , jti = CONCAT("+sessionTable+".jti, ',' , ?), exp = ?"; + update(start, QUERY, pst -> { + String jtiDbValue = jtis == null ? null : String.join(",", jtis); + + pst.setString(1, gid); + pst.setString(2, clientId); + pst.setString(3, appIdentifier.getAppId()); + pst.setString(4, externalRefreshToken); + pst.setString(5, internalRefreshToken); + pst.setString(6, sessionHandle); + pst.setString(7, jtiDbValue); + pst.setLong(8, exp); + + pst.setString(9, externalRefreshToken); + pst.setString(10, internalRefreshToken); + pst.setString(11, sessionHandle); + pst.setString(12, jtiDbValue); + pst.setLong(13, exp); + }); + } + + public static List getOAuthClients(Start start, AppIdentifier appIdentifier, List clientIds) + throws SQLException, StorageQueryException { + if(clientIds.isEmpty()){ + return Collections.emptyList(); + } + String QUERY = "SELECT * FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ? AND client_id IN ( " + + Utils.generateCommaSeperatedQuestionMarks(clientIds.size()) + + " );"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < clientIds.size(); i++) { + pst.setString(i + 2, clientIds.get(i)); + } + System.out.println(pst); + }, (result) -> { + List res = new ArrayList<>(); + while (result.next()) { + res.add(new OAuthClient(result.getString("client_id"), result.getString("client_secret"), result.getBoolean("is_client_credentials_only"), result.getBoolean("enable_refresh_token_rotation"))); + } + return res; + }); + } + + public static void addOrUpdateOauthClient(Start start, AppIdentifier appIdentifier, String clientId, String clientSecret, + boolean isClientCredentialsOnly, boolean enableRefreshTokenRotation) + throws SQLException, StorageQueryException { + String INSERT = "INSERT INTO " + Config.getConfig(start).getOAuthClientsTable() + + "(app_id, client_id, client_secret, is_client_credentials_only, enable_refresh_token_rotation) VALUES(?, ?, ?, ?, ?) " + + "ON CONFLICT (app_id, client_id) DO UPDATE SET client_secret = ?, is_client_credentials_only = ?, enable_refresh_token_rotation = ?"; + update(start, INSERT, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, clientId); + pst.setString(3, clientSecret); + pst.setBoolean(4, isClientCredentialsOnly); + pst.setBoolean(5, enableRefreshTokenRotation); + pst.setString(6, clientSecret); + pst.setBoolean(7, isClientCredentialsOnly); + pst.setBoolean(8, enableRefreshTokenRotation); + }); + } + + public static boolean deleteOAuthClient(Start start, String clientId, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ? AND client_id = ?"; + int numberOfRow = update(start, DELETE, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, clientId); + }); + return numberOfRow > 0; + } + + public static boolean deleteOAuthSessionByGID(Start start, AppIdentifier appIdentifier, String gid) + throws SQLException, StorageQueryException { + String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable() + + " WHERE gid = ? and app_id = ?;"; + int numberOfRows = update(start, DELETE, pst -> { + pst.setString(1, gid); + pst.setString(2, appIdentifier.getAppId()); + }); + return numberOfRows > 0; + } + + public static boolean deleteOAuthSessionByClientId(Start start, AppIdentifier appIdentifier, String clientId) + throws SQLException, StorageQueryException { + String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable() + + " WHERE app_id = ? and client_id = ?;"; + int numberOfRows = update(start, DELETE, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, clientId); + }); + return numberOfRows > 0; + } + + public static boolean deleteOAuthSessionBySessionHandle(Start start, AppIdentifier appIdentifier, String sessionHandle) + throws SQLException, StorageQueryException { + String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable() + + " WHERE app_id = ? and session_handle = ?"; + int numberOfRows = update(start, DELETE, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, sessionHandle); + }); + return numberOfRows > 0; + } + + public static boolean deleteJTIFromOAuthSession(Start start, AppIdentifier appIdentifier, String gid, String jti) + throws SQLException, StorageQueryException { + //jti is a comma separated list. When deleting a jti, just have to delete from the list + String DELETE = "UPDATE " + Config.getConfig(start).getOAuthSessionsTable() + + " SET jti = REPLACE(jti, ?, '')" // deletion means replacing the jti with empty char + + " WHERE app_id = ? and gid = ?"; + int numberOfRows = update(start, DELETE, pst -> { + pst.setString(1, jti); + pst.setString(2, appIdentifier.getAppId()); + pst.setString(3, gid); + }); + return numberOfRows > 0; + } + + public static int countTotalNumberOfClients(Start start, AppIdentifier appIdentifier, + boolean filterByClientCredentialsOnly) throws SQLException, StorageQueryException { + if (filterByClientCredentialsOnly) { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ? AND is_client_credentials_only = ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setBoolean(2, true); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } else { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthClientsTable() + + " WHERE app_id = ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } + } + + public static int countTotalNumberOfOAuthM2MTokensAlive(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthM2MTokensTable() + + " WHERE app_id = ? AND exp > ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setLong(2, System.currentTimeMillis()/1000); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } + + public static int countTotalNumberOfOAuthM2MTokensCreatedSince(Start start, AppIdentifier appIdentifier, long since) + throws SQLException, StorageQueryException { + String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthM2MTokensTable() + + " WHERE app_id = ? AND iat >= ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setLong(2, since / 1000); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } + + public static void addOAuthM2MTokenForStats(Start start, AppIdentifier appIdentifier, String clientId, long iat, long exp) + throws SQLException, StorageQueryException { + String QUERY = "INSERT INTO " + Config.getConfig(start).getOAuthM2MTokensTable() + + " (app_id, client_id, iat, exp) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING"; + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, clientId); + pst.setLong(3, iat); + pst.setLong(4, exp); + }); + } + + public static void addOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge, String clientId, + String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) throws SQLException, StorageQueryException { + String QUERY = "INSERT INTO " + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " (app_id, challenge, client_id, post_logout_redirect_uri, session_handle, state, time_created) VALUES (?, ?, ?, ?, ?, ?, ?)"; + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, challenge); + pst.setString(3, clientId); + pst.setString(4, postLogoutRedirectionUri); + pst.setString(5, sessionHandle); + pst.setString(6, state); + pst.setLong(7, timeCreated); + }); + } + + public static OAuthLogoutChallenge getOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge) throws SQLException, StorageQueryException { + String QUERY = "SELECT challenge, client_id, post_logout_redirect_uri, session_handle, state, time_created FROM " + + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " WHERE app_id = ? AND challenge = ?"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, challenge); + }, result -> { + if (result.next()) { + return new OAuthLogoutChallenge( + result.getString("challenge"), + result.getString("client_id"), + result.getString("post_logout_redirect_uri"), + result.getString("session_handle"), + result.getString("state"), + result.getLong("time_created") + ); + } + return null; + }); + } + + public static void deleteOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge) throws SQLException, StorageQueryException { + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " WHERE app_id = ? AND challenge = ?"; + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, challenge); + }); + } + + public static void deleteOAuthLogoutChallengesBefore(Start start, long time) throws SQLException, StorageQueryException { + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthLogoutChallengesTable() + + " WHERE time_created < ?"; + update(start, QUERY, pst -> { + pst.setLong(1, time); + }); + } + + public static String getRefreshTokenMapping(Start start, AppIdentifier appIdentifier, String externalRefreshToken) throws SQLException, StorageQueryException { + String QUERY = "SELECT internal_refresh_token FROM " + Config.getConfig(start).getOAuthSessionsTable() + + " WHERE app_id = ? AND external_refresh_token = ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, externalRefreshToken); + }, result -> { + if (result.next()) { + return result.getString("internal_refresh_token"); + } + return null; + }); + } + + public static void deleteExpiredOAuthSessions(Start start, long exp) throws SQLException, StorageQueryException { + // delete expired M2M tokens + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable() + + " WHERE exp < ?"; + + update(start, QUERY, pst -> { + pst.setLong(1, exp); + }); + } + + public static void deleteExpiredOAuthM2MTokens(Start start, long exp) throws SQLException, StorageQueryException { + // delete expired M2M tokens + String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthM2MTokensTable() + + " WHERE exp < ?"; + update(start, QUERY, pst -> { + pst.setLong(1, exp); + }); + } + + public static boolean isOAuthSessionExistsByJTI(Start start, AppIdentifier appIdentifier, String gid, String jti) + throws SQLException, StorageQueryException { + String SELECT = "SELECT jti FROM " + Config.getConfig(start).getOAuthSessionsTable() + + " WHERE app_id = ? and gid = ?;"; + return execute(start, SELECT, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, gid); + }, result -> { + if(result.next()){ + List jtis = Arrays.stream(result.getString(1).split(",")).filter(s -> !s.isEmpty()).collect( + Collectors.toList()); + return jtis.contains(jti); + } + return false; + }); + } + + public static boolean isOAuthSessionExistsByGID(Start start, AppIdentifier appIdentifier, String gid) + throws SQLException, StorageQueryException { + String SELECT = "SELECT count(*) FROM " + Config.getConfig(start).getOAuthSessionsTable() + + " WHERE app_id = ? and gid = ?;"; + return execute(start, SELECT, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, gid); + }, result -> { + if(result.next()){ + return result.getInt(1) > 0; + } + return false; + }); + } + +} From a9d72a74f5878e098a25ea8a2d7a8e17269aac61 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Sun, 27 Oct 2024 01:18:18 +0200 Subject: [PATCH 27/45] fix: fixing auto-merge issues --- CHANGELOG.md | 19 +- .../supertokens/storage/postgresql/Start.java | 185 ------------------ .../postgresql/config/PostgreSQLConfig.java | 12 -- 3 files changed, 11 insertions(+), 205 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f45ac6..6654366e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,18 +23,21 @@ CREATE TABLE IF NOT EXISTS oauth_clients ( FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS oauth_revoke ( +CREATE TABLE IF NOT EXISTS oauth_sessions ( + gid VARCHAR(255), app_id VARCHAR(64) DEFAULT 'public', - target_type VARCHAR(16) NOT NULL, - target_value VARCHAR(128) NOT NULL, - timestamp BIGINT NOT NULL, + client_id VARCHAR(255) NOT NULL, + session_handle VARCHAR(128), + external_refresh_token VARCHAR(255) UNIQUE, + internal_refresh_token VARCHAR(255) UNIQUE, + jti TEXT NOT NULL, exp BIGINT NOT NULL, - PRIMARY KEY (app_id, target_type, target_value), - FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE + PRIMARY KEY (gid), + FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE ); -CREATE INDEX IF NOT EXISTS oauth_revoke_timestamp_index ON oauth_revoke(timestamp DESC, app_id DESC); -CREATE INDEX IF NOT EXISTS oauth_revoke_exp_index ON oauth_revoke(exp DESC); +CREATE INDEX IF NOT EXISTS oauth_session_exp_index ON oauth_sessions(exp DESC); +CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON oauth_sessions(app_id, external_refresh_token DESC); CREATE TABLE IF NOT EXISTS oauth_m2m_tokens ( app_id VARCHAR(64) DEFAULT 'public', diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index fc2ee5fa..a7800301 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -3101,33 +3101,6 @@ public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(A } } - @Override - public boolean doesOAuthClientIdExist(AppIdentifier appIdentifier, String clientId) - throws StorageQueryException { - try { - return OAuthQueries.doesOAuthClientIdExist(this, clientId, appIdentifier); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public void addOrUpdateOauthClient(AppIdentifier appIdentifier, String clientId, boolean isClientCredentialsOnly) - throws StorageQueryException, TenantOrAppNotFoundException { - try { - OAuthQueries.addOrUpdateOauthClient(this, appIdentifier, clientId, isClientCredentialsOnly); - } catch (SQLException e) { - PostgreSQLConfig config = Config.getConfig(this); - if (e instanceof PSQLException) { - ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); - - if (isForeignKeyConstraintError(serverMessage, config.getOAuthClientsTable(), "app_id")) { - throw new TenantOrAppNotFoundException(appIdentifier); - } - } - throw new StorageQueryException(e); - } - } @Override public boolean deleteOAuthClient(AppIdentifier appIdentifier, String clientId) throws StorageQueryException { @@ -3138,156 +3111,7 @@ public boolean deleteOAuthClient(AppIdentifier appIdentifier, String clientId) t } } - @Override - public List listOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException { - try { - return OAuthQueries.listOAuthClients(this, appIdentifier); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public void revokeOAuthTokensBasedOnTargetFields(AppIdentifier appIdentifier, OAuthRevokeTargetType targetType, String targetValue, long exp) - throws StorageQueryException, TenantOrAppNotFoundException { - try { - OAuthQueries.revokeOAuthTokensBasedOnTargetFields(this, appIdentifier, targetType, targetValue, exp); - } catch (SQLException e) { - PostgreSQLConfig config = Config.getConfig(this); - if (e instanceof PSQLException) { - ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); - - if (isForeignKeyConstraintError(serverMessage, config.getOAuthRevokeTable(), "app_id")) { - throw new TenantOrAppNotFoundException(appIdentifier); - } - } - throw new StorageQueryException(e); - } - - } - - @Override - public boolean isOAuthTokenRevokedBasedOnTargetFields(AppIdentifier appIdentifier, OAuthRevokeTargetType[] targetTypes, String[] targetValues, long issuedAt) - throws StorageQueryException { - try { - return OAuthQueries.isOAuthTokenRevokedBasedOnTargetFields(this, appIdentifier, targetTypes, targetValues, issuedAt); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public void addOAuthM2MTokenForStats(AppIdentifier appIdentifier, String clientId, long iat, long exp) - throws StorageQueryException, OAuthClientNotFoundException { - try { - OAuthQueries.addOAuthM2MTokenForStats(this, appIdentifier, clientId, iat, exp); - } catch (SQLException e) { - PostgreSQLConfig config = Config.getConfig(this); - if (e instanceof PSQLException) { - ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); - if (isForeignKeyConstraintError(serverMessage, config.getOAuthM2MTokensTable(), "client_id")) { - throw new OAuthClientNotFoundException(); - } - } - throw new StorageQueryException(e); - } - } - - @Override - public void cleanUpExpiredAndRevokedOAuthTokensList() throws StorageQueryException { - try { - OAuthQueries.cleanUpExpiredAndRevokedOAuthTokensList(this); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public void addOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge, String clientId, - String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) - throws StorageQueryException, DuplicateOAuthLogoutChallengeException, OAuthClientNotFoundException { - try { - OAuthQueries.addOAuthLogoutChallenge(this, appIdentifier, challenge, clientId, postLogoutRedirectionUri, sessionHandle, state, timeCreated); - } catch (SQLException e) { - PostgreSQLConfig config = Config.getConfig(this); - if (e instanceof PSQLException) { - ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); - - if (isPrimaryKeyError(serverMessage, config.getOAuthLogoutChallengesTable())) { - throw new DuplicateOAuthLogoutChallengeException(); - } else if (isForeignKeyConstraintError(serverMessage, config.getOAuthLogoutChallengesTable(), "client_id")) { - throw new OAuthClientNotFoundException(); - } - } - throw new StorageQueryException(e); - } - } - - @Override - public OAuthLogoutChallenge getOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException { - try { - return OAuthQueries.getOAuthLogoutChallenge(this, appIdentifier, challenge); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public void deleteOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException { - try { - OAuthQueries.deleteOAuthLogoutChallenge(this, appIdentifier, challenge); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public void deleteOAuthLogoutChallengesBefore(long time) throws StorageQueryException { - try { - OAuthQueries.deleteOAuthLogoutChallengesBefore(this, time); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public int countTotalNumberOfOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException { - try { - return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, false); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public int countTotalNumberOfClientCredentialsOnlyOAuthClients(AppIdentifier appIdentifier) - throws StorageQueryException { - try { - return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, true); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public int countTotalNumberOfOAuthM2MTokensCreatedSince(AppIdentifier appIdentifier, long since) - throws StorageQueryException { - try { - return OAuthQueries.countTotalNumberOfOAuthM2MTokensCreatedSince(this, appIdentifier, since); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public int countTotalNumberOfOAuthM2MTokensAlive(AppIdentifier appIdentifier) throws StorageQueryException { - try { - return OAuthQueries.countTotalNumberOfOAuthM2MTokensAlive(this, appIdentifier); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } @TestOnly public int getDbActivityCount(String dbname) throws SQLException, StorageQueryException { @@ -3335,15 +3159,6 @@ public void addOrUpdateOauthClient(AppIdentifier appIdentifier, String clientId, } } - @Override - public boolean deleteOAuthClient(AppIdentifier appIdentifier, String clientId) throws StorageQueryException { - try { - return OAuthQueries.deleteOAuthClient(this, clientId, appIdentifier); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - @Override public List getOAuthClients(AppIdentifier appIdentifier, List clientIds) throws StorageQueryException { try { 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 3cdf80aa..095badc8 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -434,22 +434,10 @@ public String getDashboardSessionsTable() { return addSchemaAndPrefixToTableName("dashboard_user_sessions"); } - public String getOAuthClientsTable() { - return addSchemaAndPrefixToTableName("oauth_clients"); - } - public String getOAuthRevokeTable() { return addSchemaAndPrefixToTableName("oauth_revoke"); } - public String getOAuthM2MTokensTable() { - return addSchemaAndPrefixToTableName("oauth_m2m_tokens"); - } - - public String getOAuthLogoutChallengesTable() { - return addSchemaAndPrefixToTableName("oauth_logout_challenges"); - } - public String getTotpUsersTable() { return addSchemaAndPrefixToTableName("totp_users"); } From ffa0196dc3b9e7ec79238a2eee3aa662a4b66fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20Lengyel?= Date: Sun, 27 Oct 2024 02:01:26 +0100 Subject: [PATCH 28/45] Update src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java --- .../storage/postgresql/config/PostgreSQLConfig.java | 3 --- 1 file changed, 3 deletions(-) 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 095badc8..89aad8b7 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -434,9 +434,6 @@ public String getDashboardSessionsTable() { return addSchemaAndPrefixToTableName("dashboard_user_sessions"); } - public String getOAuthRevokeTable() { - return addSchemaAndPrefixToTableName("oauth_revoke"); - } public String getTotpUsersTable() { return addSchemaAndPrefixToTableName("totp_users"); From beb503dc60428b64d7a22d08d2d59b93e4b24326 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Sun, 27 Oct 2024 02:07:15 +0100 Subject: [PATCH 29/45] fix: merge error --- .../supertokens/storage/postgresql/queries/GeneralQueries.java | 1 - 1 file changed, 1 deletion(-) 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 3430c41b..83d8bc0e 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -656,7 +656,6 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer + getConfig(start).getDashboardUsersTable() + "," + getConfig(start).getDashboardSessionsTable() + "," + getConfig(start).getOAuthClientsTable() + "," - + getConfig(start).getOAuthRevokeTable() + "," + getConfig(start).getOAuthM2MTokensTable() + "," + getConfig(start).getOAuthLogoutChallengesTable() + "," + getConfig(start).getTotpUsedCodesTable() + "," From 60d436348635dec9035c087de92e4c6b93b4d262 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Sun, 27 Oct 2024 02:07:27 +0100 Subject: [PATCH 30/45] adding dev-v7.2.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.2.0.jar | Bin 232868 -> 235310 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.2.0.jar b/jar/postgresql-plugin-7.2.0.jar index 86ad2a0a500f6426012719724c6cf997df888727..032449c323d0f1677dde8ac97d3a43b76339bae6 100644 GIT binary patch delta 76703 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 delta 73918 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%0ARean|i?(00&VQN4Soe%Y_2U8C`4Fu8&P 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 KQ*Llq0sjN3KTC@M From e21f4bb4cf61442fead0e936743c798ea39552e7 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Sun, 27 Oct 2024 02:08:07 +0100 Subject: [PATCH 31/45] adding dev-v7.2.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.2.0.jar | Bin 235310 -> 235310 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.2.0.jar b/jar/postgresql-plugin-7.2.0.jar index 032449c323d0f1677dde8ac97d3a43b76339bae6..85e6e464e030e16afed7fddaa9459999a9a63e12 100644 GIT binary patch delta 1608 zcmY+DeN0LnZaPtO9^=liR3})v=Nq%GMw#`}FJ(S1eJi{4pqNCg#UZvNMLg0paHumM=x% z)E36)e$NikXg1{4O_rcE#WD|aE5-!&Hi}X5_OoFV0_}1384@dBnpu$n!QMia%RSCw zHp5pPE@$Vs60ovat`yl=nvrKJ-uk6BPrVZhHQz70zBi{yV=4dm}q=`{$EX;grlRO}fPp+(>}lZ)54Gn+0N;k{f! z@!;Pj)C8855fgSraJ1kY3m{rd{Ca9<@Afs>U~0zPl0a(*PLVK#kyd1APvf8_5a&(MV&UyNO!CNIRVZE9@fqs$CpzPBV2u|FfA2!IKUu z1;25K<`Y}Uj`3^@^@2xQsRR76l|10tHu`{%+l22#yO>{TCkOQ04$;?Q2en|F*-7WY z^-ih;3%aP2kGsTSCeE7}?LiZ-4Z5bd@NPzLt7JwSP2 Z${@WD`Uk~3Ve*OVv->Cs`kaqS{s-BXuU-HE delta 1608 zcmY+DeN0Fg-eap4&n%<#Sp;uH?Ks zlFNB=YzFk`CsJY0m`HjY^iIX^74kv(tc|pWN4uPUVlka(eRs*hTl?*HO(^`?fiz$| za0(#N68qC%pCKKd&;5}dq|t23t6MBVX^drV~XOhJjn4d3lDDj*s&Po+dIeZ;9M2M*Q|k0_0F^VyP!DMSQ^iFJ;0`L*h=GlHp)Hw z|7DXrxVgoKxTkXaVXgp^VsXDpRFLFIL@;<*`im>hWGRuanop4)@T6B>k)FW?D9eMA zKMFzTlr*ykLCOv38aIdDlD^@=;<9u-lE0RLB6t6o+V+B0{xL<0S61#xOWdMtJX~bo zl=STDqF?gnoYQUv3$_JCn zsQ~=6oJ^q3LYd$>i_m|tkeT}`s0f^_pc3#oE0u$NRyqrASSbs9qmnAYYn422`KS~>?l zTSr!~ua3N6bUihIRPy+?*!rgudQHh2Uui zm4e?oMDvNwWXE{6nR>uuEz}PF)Ix6Xd@H@r$F0J5s!hzVw2=dPZoBAfv7MSR&g`HM zz>N;701G;)gO5AKVdXlh3*&oEQJ1Yt)cIc*H9>Fa7X1YEh&ru3+~?~R=^edP4;yod zc8XoXw&J1-(9_&h3(mWR?`WSm?#Vtej_nueCi_J>MvrJ`)Faw>+AG>^^->1*x$UJq aFlB(=14jnLJ7M&R>$CeP3HqFmO8y75T2Q?J From c98c4475059f2c2b1fbd6b8da9ce0317e68840fe Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Mon, 28 Oct 2024 09:21:35 +0100 Subject: [PATCH 32/45] fix: changinf jti + comma handling --- .../io/supertokens/storage/postgresql/Start.java | 4 ++-- .../storage/postgresql/queries/OAuthQueries.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index a7800301..ade74a27 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -3287,11 +3287,11 @@ public void deleteOAuthLogoutChallengesBefore(long time) throws StorageQueryExce @Override public void createOrUpdateOAuthSession(AppIdentifier appIdentifier, String gid, String clientId, String externalRefreshToken, String internalRefreshToken, - String sessionHandle, List jtis, long exp) + String sessionHandle, String jti, long exp) throws StorageQueryException, OAuthClientNotFoundException { try { OAuthQueries.createOrUpdateOAuthSession(this, appIdentifier, gid, clientId, externalRefreshToken, - internalRefreshToken, sessionHandle, jtis, exp); + internalRefreshToken, sessionHandle, jti, exp); } catch (SQLException e) { ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage(); PostgreSQLConfig config = Config.getConfig(this); diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java index 58a26efd..43c66432 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java @@ -163,15 +163,15 @@ public static OAuthClient getOAuthClientById(Start start, String clientId, AppId public static void createOrUpdateOAuthSession(Start start, AppIdentifier appIdentifier, @NotNull String gid, @NotNull String clientId, String externalRefreshToken, String internalRefreshToken, String sessionHandle, - List jtis, long exp) + String jti, long exp) throws SQLException, StorageQueryException { String sessionTable = Config.getConfig(start).getOAuthSessionsTable(); String QUERY = "INSERT INTO " + sessionTable + " (gid, client_id, app_id, external_refresh_token, internal_refresh_token, session_handle, jti, exp) VALUES (?, ?, ?, ?, ?, ?, ?, ?) " + "ON CONFLICT (gid) DO UPDATE SET external_refresh_token = ?, internal_refresh_token = ?, " + - "session_handle = ? , jti = CONCAT("+sessionTable+".jti, ',' , ?), exp = ?"; + "session_handle = ? , jti = CONCAT("+sessionTable+".jti, ?), exp = ?"; update(start, QUERY, pst -> { - String jtiDbValue = jtis == null ? null : String.join(",", jtis); + String jtiToInsert = jti + ","; pst.setString(1, gid); pst.setString(2, clientId); @@ -179,13 +179,13 @@ public static void createOrUpdateOAuthSession(Start start, AppIdentifier appIden pst.setString(4, externalRefreshToken); pst.setString(5, internalRefreshToken); pst.setString(6, sessionHandle); - pst.setString(7, jtiDbValue); + pst.setString(7, jtiToInsert); //the starting list element also has to have a "," at the end as the remove removes "jti + ," pst.setLong(8, exp); pst.setString(9, externalRefreshToken); pst.setString(10, internalRefreshToken); pst.setString(11, sessionHandle); - pst.setString(12, jtiDbValue); + pst.setString(12, jtiToInsert); pst.setLong(13, exp); }); } @@ -283,7 +283,7 @@ public static boolean deleteJTIFromOAuthSession(Start start, AppIdentifier appId + " SET jti = REPLACE(jti, ?, '')" // deletion means replacing the jti with empty char + " WHERE app_id = ? and gid = ?"; int numberOfRows = update(start, DELETE, pst -> { - pst.setString(1, jti); + pst.setString(1, jti + ","); //removing with the "," to not leave behind trash pst.setString(2, appIdentifier.getAppId()); pst.setString(3, gid); }); From 036200342d288b933963d895fae0642cc7837ae3 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 28 Oct 2024 17:04:23 +0530 Subject: [PATCH 33/45] fix: flaky test --- .../storage/postgresql/test/DbConnectionPoolTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java index 2a8aa753..3a7f5935 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java @@ -287,6 +287,16 @@ public void testMinimumIdleConnectionForTenants() throws Exception { Thread.sleep(1000); // let the new tenant be ready + for (int retry = 0; retry < 5; retry++) { + try { + assertEquals(10, start.getDbActivityCount("st1")); + break; + } catch (AssertionError e) { + Thread.sleep(1000); + continue; + } + } + assertEquals(10, start.getDbActivityCount("st1")); // change connection pool size From 29c7124e2b8057a196d39ee1c5b8e3740a7e0444 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 28 Oct 2024 12:50:02 +0100 Subject: [PATCH 34/45] adding dev-v7.2.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.2.0.jar | Bin 235310 -> 235208 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.2.0.jar b/jar/postgresql-plugin-7.2.0.jar index 85e6e464e030e16afed7fddaa9459999a9a63e12..63ddfdd08e0c4cd4904d552a482b61cfc6c74525 100644 GIT binary patch delta 42923 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 delta 42953 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 Date: Mon, 28 Oct 2024 17:20:07 +0100 Subject: [PATCH 35/45] ci: add missing image to ci config --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8fd9177a..5bcca6e9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,6 +5,7 @@ jobs: test: docker: - image: rishabhpoddar/supertokens_postgresql_plugin_test + - image: rishabhpoddar/oauth-server-cicd resource_class: large steps: - add_ssh_keys: From dc86ab9e3a27bb2326555bdeb2bba14bb7777485 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 28 Oct 2024 17:20:35 +0100 Subject: [PATCH 36/45] fix: remove System.out --- .../io/supertokens/storage/postgresql/queries/OAuthQueries.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java index 43c66432..c6f59566 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java @@ -204,7 +204,6 @@ public static List getOAuthClients(Start start, AppIdentifier appId for (int i = 0; i < clientIds.size(); i++) { pst.setString(i + 2, clientIds.get(i)); } - System.out.println(pst); }, (result) -> { List res = new ArrayList<>(); while (result.next()) { From 609c0949e4e58da74969bd5e88651ae064911a77 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 28 Oct 2024 17:27:09 +0100 Subject: [PATCH 37/45] adding dev-v7.2.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.2.0.jar | Bin 235208 -> 235144 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.2.0.jar b/jar/postgresql-plugin-7.2.0.jar index 63ddfdd08e0c4cd4904d552a482b61cfc6c74525..72650218b5986accd4768775e456bb5d81993a50 100644 GIT binary patch delta 9925 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 Date: Tue, 29 Oct 2024 02:53:23 +0100 Subject: [PATCH 38/45] chore: changelog consistency --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6654366e..af622075 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ```sql CREATE TABLE IF NOT EXISTS oauth_clients ( app_id VARCHAR(64), - client_id VARCHAR(128) NOT NULL, + client_id VARCHAR(255) NOT NULL, is_client_credentials_only BOOLEAN NOT NULL, PRIMARY KEY (app_id, client_id), FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE @@ -41,7 +41,7 @@ CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON oauth_s CREATE TABLE IF NOT EXISTS oauth_m2m_tokens ( app_id VARCHAR(64) DEFAULT 'public', - client_id VARCHAR(128) NOT NULL, + client_id VARCHAR(255) NOT NULL, iat BIGINT NOT NULL, exp BIGINT NOT NULL, PRIMARY KEY (app_id, client_id, iat), @@ -54,7 +54,7 @@ CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON oauth_m2m_tokens(exp DES CREATE TABLE IF NOT EXISTS oauth_logout_challenges ( app_id VARCHAR(64) DEFAULT 'public', challenge VARCHAR(128) NOT NULL, - client_id VARCHAR(128) NOT NULL, + client_id VARCHAR(255) NOT NULL, post_logout_redirect_uri VARCHAR(1024), session_handle VARCHAR(128), state VARCHAR(128), From 71a33d83110735dc6ad95312bd1b4d1490b8a3e5 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 30 Oct 2024 15:30:50 +0530 Subject: [PATCH 39/45] adding dev-v7.2.0 tag to this commit to ensure building --- jar/postgresql-plugin-7.2.0.jar | Bin 235144 -> 235144 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/jar/postgresql-plugin-7.2.0.jar b/jar/postgresql-plugin-7.2.0.jar index 72650218b5986accd4768775e456bb5d81993a50..d8e15676835bb46788d7664ac2c22573c6de8ff7 100644 GIT binary patch delta 4016 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 XE7p@IyjT%@NP6~>C{DQ7kgT5p*4pa4 delta 3967 zcmZWr2~<_*5&jnilzkTgg$M)`1q5RhpDb}fS=4AXAW)6gY8yZZ2xt(V3m_;0Px#fi zkYXdjh=f;to*W{28m&}Cqj9B*#s)QNQfVS?)y|!JSuVVD;Le%(=9}f8f9@?jXi<33 z!hCilA265@6BDwuIKIr>i%XApW_rORmcWlm|KP{rZ$*k9Cu)b!ebM1^ko3@*(GcmW zHv|kGAPI)74z`h^)$n~Cf)?qJ9~!^~uG!6{}yN%v~tG)y+r zTQdlSsYZTpNdi~`g~cvrixKzMuqe6YpARst+z^jlEXxd`PbN*5p5mm2&O^A%Wcv5P zD9wqZopKjW$IyIJEV(#fRBiY`Lek9$$=3yl(V{Ak4>wGjK{v_J$VD{N6y1ZeXsQuP zC4Z*X7)@BY(r8pIa;3|dxm5D`{m0uA1`yJzbGsX$FD)+}fSss$-D!IRPJMxqxKqyj zb*1H&`o>xlEaY$s-Q$8ZP2ud2ob0U;^iLM3JAaEVMwtE|2Muz$soU8ftf6$5X^}!m)-(k=jKhlE^D2=&FPswDRli5jG zD)eAy?9tjhoZ4Vey~AAv=H5UDd!jLNEREoJ8abCN5+Bu4laYAitm*rdNr4GC#)*30;mqzpz=focSL1q8YoY1mXx~Aan52_8aKlde-)SS zIRbAo@J>uw~v?umS+G%2InUeT`sP8u}1m1XPNtlY;^K+Tt)G21$FVE1;K-RV&kH^o*xSu^4 z29$Z%VCxU1^5K{lm6XfB>T=TM8aasHrz%M!cUr`R^cTv&a&f&u_;?0IP#_2GE#+HOE?%E zU8WH+=}IzZasfb1`v5G(={#jRZ{`7DibD@zsEE<>Aa`lIT&sTb#2=a(@irkJs1iBq z(&HfCy>r>Xljs94e$6NWpCR(>k6I1dIz{`bE43GKTb0cI^)Z+nebE74k3Ky)pHBzN zkN#a2OI;%@vhIo&OMxZ^by+RM(QGByg+j2j&ZfdToBN5^;mlqf%jRZSid%U4WOEBb z9>Zy?x4@Q#a?Xp^a1Wf?*bPAQVg(i2?QE)O+`#c&E#fjjotUJkzscY}NE{eFG3^OV zMzayTb5%VWl7`rL(U_~;y|S$3tAk-_41vR4s=}_z5cQw%G>o6K4g!U&M(1Gh&sE$8 z10}t_lf4Y~bshwor*hs@Om$@q5n@&=H5W5CDE)0)1vHDdN~Tch6TlnEA4pY<(@EL| zJv~j5u(g$vHl!!wWu57YOjs4)LU{{R5uY|mP=AqEth~(5#ou_^k(PF06``Wp<$e zc)ob9*m8>apgs+^`UC(oU#kF8U!^Y!_afK{ja1Aj30!Wov&E)zh>nqHu9vbdFQChHd8tEms-FihN>}^qJ1}Y#unm&p=s7H zis4}Y3I{t!Rr`+j)SiJDcqLp_rkkoZ$rlg#+FW7J9X-* zCT=~DCVbV)USGgXMcsO(C3E05Gq1ya?V1qg(o@O8>ESII_JkzP>iZG%^HH^@D^{|7 zHN0+HdRgKdsI{2ZN*(axU^O2-kGu7O^kHN|$Xr!5-(BqIxk_0Y58=|XombR$N30e} z6W$+v`y#aaP`@>?|79;H_%hXCq6x4AS;uR&$xeKou55!s&%J>Wc7#O2S6`?q$uJu( zEx>4;^6;nP-5bLRX-Fnyk_vXuiwkW&pMlY4mcX1kPv>ywMu_4PWuI0_+9is%qk#>< zO@_QvKAyGc7?e_NPDr4Nw`H!(lF$S(4x+BCQFT;o0}i XofO}Vw-d$NlnHOGP+H_%$&>yMuVA?{ From b83a780a232fcde6f8e013df8f2748f5058642db Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Tue, 5 Nov 2024 16:09:53 +0100 Subject: [PATCH 40/45] chore: current state save --- .../postgresql/BulkImportProxyConnection.java | 22 ++----- .../postgresql/BulkImportProxyStorage.java | 24 +++++--- .../supertokens/storage/postgresql/Start.java | 13 ++-- .../postgresql/queries/GeneralQueries.java | 61 +++++++++++++++---- 4 files changed, 75 insertions(+), 45 deletions(-) diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java index b25df932..e7fa3d48 100644 --- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java +++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java @@ -16,21 +16,7 @@ package io.supertokens.storage.postgresql; -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.SQLClientInfoException; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Savepoint; -import java.sql.Statement; -import java.sql.Struct; +import java.sql.*; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; @@ -53,17 +39,17 @@ public BulkImportProxyConnection(Connection con) { @Override public void close() throws SQLException { - // We simply ignore when close is called BulkImportProxyConnection +// this.con.close(); } @Override public void commit() throws SQLException { - // We simply ignore when commit is called BulkImportProxyConnection +// this.con.commit(); } @Override public void rollback() throws SQLException { - // We simply ignore when rollback is called BulkImportProxyConnection +// this.con.rollback(); } public void closeForBulkImportProxyStorage() throws SQLException { diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java index 59f834a7..4e2493ce 100644 --- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java +++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java @@ -16,21 +16,16 @@ package io.supertokens.storage.postgresql; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.List; -import java.util.Set; - -import com.google.gson.JsonObject; - -import io.supertokens.pluginInterface.LOG_LEVEL; import io.supertokens.pluginInterface.exceptions.DbInitException; -import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + /** * BulkImportProxyStorage is a class extending Start, serving as a Storage instance in the bulk import user cronjob. @@ -62,8 +57,9 @@ protected T startTransactionHelper(TransactionLogic logic, TransactionIso @Override public void commitTransaction(TransactionConnection con) throws StorageQueryException { - // We do not want to commit the queries when using the BulkImportProxyStorage to be able to rollback everything + // We do not want to commit the queries when using the BulkImportProxyStorage to be able to rollback everything // if any query fails while importing the user +// super.commitTransaction(con); } @Override @@ -114,4 +110,12 @@ public void rollbackTransactionForBulkImportProxyStorage() throws StorageQueryEx throw new StorageQueryException(e); } } + + public void doVacuumFull() throws StorageQueryException { + try { + this.connection.prepareStatement("VACUUM FULL").execute(); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } } diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 206f58a4..35cd4edf 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -26,10 +26,9 @@ import io.supertokens.pluginInterface.authRecipe.LoginMethod; import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage; import io.supertokens.pluginInterface.bulkimport.BulkImportStorage; -import io.supertokens.pluginInterface.bulkimport.BulkImportStorage.BULK_IMPORT_USER_STATUS; +import io.supertokens.pluginInterface.bulkimport.BulkImportUser; import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportTransactionRolledBackException; import io.supertokens.pluginInterface.bulkimport.sqlStorage.BulkImportSQLStorage; -import io.supertokens.pluginInterface.bulkimport.BulkImportUser; import io.supertokens.pluginInterface.dashboard.DashboardSearchTags; import io.supertokens.pluginInterface.dashboard.DashboardSessionInfo; import io.supertokens.pluginInterface.dashboard.DashboardUser; @@ -53,7 +52,10 @@ import io.supertokens.pluginInterface.jwt.JWTSigningKeyInfo; import io.supertokens.pluginInterface.jwt.exceptions.DuplicateKeyIdException; import io.supertokens.pluginInterface.jwt.sqlstorage.JWTRecipeSQLStorage; -import io.supertokens.pluginInterface.multitenancy.*; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.MultitenancyStorage; +import io.supertokens.pluginInterface.multitenancy.TenantConfig; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateClientTypeException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException; @@ -103,7 +105,10 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLTransactionRollbackException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; 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 730210dd..44dc3c4e 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -103,8 +103,16 @@ static String getQueryToCreateUsersTable(Start start) { public static String getQueryToCreateUserIdIndexForUsersTable(Start start) { return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_index ON " + + Config.getConfig(start).getUsersTable() + "(user_id);"; + } + public static String getQueryToCreateUserIdAppIdIndexForUsersTable(Start start) { + return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_app_id_index ON " + Config.getConfig(start).getUsersTable() + "(app_id, user_id);"; } + public static String getQueryToCreateAppIdIndexForUsersTable(Start start) { + return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_app_id_index ON " + + Config.getConfig(start).getUsersTable() + "(app_id);"; + } public static String getQueryToCreateTenantIdIndexForUsersTable(Start start) { return "CREATE INDEX IF NOT EXISTS all_auth_recipe_tenant_id_index ON " @@ -247,6 +255,11 @@ static String getQueryToCreatePrimaryUserIdIndexForAppIdToUserIdTable(Start star + Config.getConfig(start).getAppIdToUserIdTable() + "(primary_or_recipe_user_id, app_id);"; } + static String getQueryToCreateUserIdIndexForAppIdToUserIdTable(Start start) { + return "CREATE INDEX IF NOT EXISTS app_id_to_user_id_user_id_index ON " + + Config.getConfig(start).getAppIdToUserIdTable() + "(user_id, app_id);"; + } + public static void createTablesIfNotExists(Start start, Connection con) throws SQLException, StorageQueryException { int numberOfRetries = 0; boolean retry = true; @@ -281,6 +294,7 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S // index update(con, getQueryToCreateAppIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER); update(con, getQueryToCreatePrimaryUserIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER); + update(con, getQueryToCreateUserIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER); } if (!doesTableExists(start, con, Config.getConfig(start).getUsersTable())) { @@ -433,6 +447,8 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S // index update(con, getQueryToCreateUserIdIndexForUsersTable(start), NO_OP_SETTER); + update(con, getQueryToCreateUserIdAppIdIndexForUsersTable(start), NO_OP_SETTER); + update(con, getQueryToCreateAppIdIndexForUsersTable(start), NO_OP_SETTER); update(con, getQueryToCreateTenantIdIndexForUsersTable(start), NO_OP_SETTER); } @@ -1534,18 +1550,37 @@ private static List getPrimaryUserInfoForUserIds_Transaction // which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column, // or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id // column - String QUERY = - "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + - "aaru.tenant_id, aaru.time_joined FROM " + - getConfig(start).getAppIdToUserIdTable() + " as au" + - " LEFT JOIN " + getConfig(start).getUsersTable() + - " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + - " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + - getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN (" - + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ") OR primary_or_recipe_user_id IN (" + - Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + - ")) AND app_id = ?) AND au.app_id = ?"; +// String QUERY = +// "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + +// "aaru.tenant_id, aaru.time_joined " + +// "FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" + +// " LEFT JOIN " + getConfig(start).getUsersTable() + +// " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + +// " WHERE au.primary_or_recipe_user_id IN " + +// " (SELECT primary_or_recipe_user_id FROM " + +// getConfig(start).getAppIdToUserIdTable() + +// " WHERE (user_id IN (" +// + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +") " + +// " OR primary_or_recipe_user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")) " + +// " AND app_id = ?) " + +// "AND au.app_id = ?"; + + String QUERY = "SELECT" + + " au.user_id," + + " au.primary_or_recipe_user_id," + + " au.is_linked_or_is_a_primary_user," + + " au.recipe_id," + + " aaru.tenant_id," + + " aaru.time_joined" + + " FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" + + " LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id" + + " AND au.user_id = aaru.user_id" + + " LEFT JOIN " + getConfig(start).getAppIdToUserIdTable() + " as aiui ON au.primary_or_recipe_user_id = aiui.user_id" + + " AND aiui.app_id = au.app_id" + + " WHERE" + + " aiui.user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + ")" + + " OR au.primary_or_recipe_user_id IN ("+ Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")" + + " AND au.app_id = ?"; List allAuthUsersResult = execute(sqlCon, QUERY, pst -> { // IN user_id @@ -1559,7 +1594,7 @@ private static List getPrimaryUserInfoForUserIds_Transaction } // for app_id pst.setString(index, appIdentifier.getAppId()); - pst.setString(index + 1, appIdentifier.getAppId()); +// System.out.println(pst); }, result -> { List parsedResult = new ArrayList<>(); while (result.next()) { From a5c740b3d8f39a7a0e1f788b9a3c33f55d18f8b4 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Tue, 5 Nov 2024 16:45:58 +0100 Subject: [PATCH 41/45] fix: fixing merge error with changelog --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af0894ed..188fd938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,7 +68,6 @@ CREATE TABLE IF NOT EXISTS oauth_logout_challenges ( CREATE INDEX IF NOT EXISTS oauth_logout_challenges_time_created_index ON oauth_logout_challenges(time_created DESC); ``` ->>>>>>> origin/master ## [7.1.3] - 2024-09-04 @@ -102,7 +101,6 @@ CREATE INDEX IF NOT EXISTS user_last_active_last_active_time_index ON user_last_ ALTER TABLE tenant_configs ADD COLUMN IF NOT EXISTS is_first_factors_null BOOLEAN DEFAULT TRUE; ALTER TABLE tenant_configs ALTER COLUMN is_first_factors_null DROP DEFAULT; ``` ->>>>>>> master ## [7.0.1] - 2024-04-17 From 57f7d04cd790b2cdba0bca011699944d28390bbb Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 15 Nov 2024 16:59:01 +0100 Subject: [PATCH 42/45] feat: bulk inserting the bulk migration data --- .../postgresql/BulkImportProxyConnection.java | 6 +- .../postgresql/BulkImportProxyStorage.java | 13 +- .../supertokens/storage/postgresql/Start.java | 175 +++++++++++++++++- .../postgresql/queries/BulkImportQueries.java | 24 +-- .../queries/EmailPasswordQueries.java | 94 +++++++++- .../queries/EmailVerificationQueries.java | 43 +++++ .../postgresql/queries/GeneralQueries.java | 110 +++++++++++ .../queries/PasswordlessQueries.java | 72 +++++++ .../postgresql/queries/TOTPQueries.java | 68 +++++-- .../postgresql/queries/ThirdPartyQueries.java | 78 +++++++- .../queries/UserMetadataQueries.java | 51 +++++ .../postgresql/queries/UserRolesQueries.java | 30 ++- 12 files changed, 716 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java index e7fa3d48..382a8f2e 100644 --- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java +++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java @@ -39,17 +39,17 @@ public BulkImportProxyConnection(Connection con) { @Override public void close() throws SQLException { -// this.con.close(); + //this.con.close(); // why are we against the close? } @Override public void commit() throws SQLException { -// this.con.commit(); + //this.con.commit(); } @Override public void rollback() throws SQLException { -// this.con.rollback(); + //this.con.rollback(); } public void closeForBulkImportProxyStorage() throws SQLException { diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java index 4e2493ce..12eeff8a 100644 --- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java +++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java @@ -20,6 +20,7 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import java.sql.Connection; @@ -51,7 +52,7 @@ public synchronized Connection getTransactionConnection() throws SQLException, S @Override protected T startTransactionHelper(TransactionLogic logic, TransactionIsolationLevel isolationLevel) - throws StorageQueryException, StorageTransactionLogicException, SQLException { + throws StorageQueryException, StorageTransactionLogicException, SQLException, TenantOrAppNotFoundException { return logic.mainLogicAndCommit(new TransactionConnection(getTransactionConnection())); } @@ -59,7 +60,7 @@ protected T startTransactionHelper(TransactionLogic logic, TransactionIso public void commitTransaction(TransactionConnection con) throws StorageQueryException { // We do not want to commit the queries when using the BulkImportProxyStorage to be able to rollback everything // if any query fails while importing the user -// super.commitTransaction(con); + //super.commitTransaction(con); } @Override @@ -110,12 +111,4 @@ public void rollbackTransactionForBulkImportProxyStorage() throws StorageQueryEx throw new StorageQueryException(e); } } - - public void doVacuumFull() throws StorageQueryException { - try { - this.connection.prepareStatement("VACUUM FULL").execute(); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } } diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 268f63b8..faa4f4aa 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -34,6 +34,7 @@ import io.supertokens.pluginInterface.dashboard.DashboardUser; import io.supertokens.pluginInterface.dashboard.exceptions.UserIdNotFoundException; import io.supertokens.pluginInterface.dashboard.sqlStorage.DashboardSQLStorage; +import io.supertokens.pluginInterface.emailpassword.EmailPasswordImportUser; import io.supertokens.pluginInterface.emailpassword.PasswordResetTokenInfo; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicatePasswordResetTokenException; @@ -68,12 +69,14 @@ import io.supertokens.pluginInterface.oauth.exception.OAuthClientNotFoundException; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; +import io.supertokens.pluginInterface.passwordless.PasswordlessImportUser; import io.supertokens.pluginInterface.passwordless.exception.*; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.session.SessionInfo; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.session.sqlStorage.SessionSQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import io.supertokens.pluginInterface.thirdparty.ThirdPartyImportUser; import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.totp.TOTPDevice; @@ -110,10 +113,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLTransactionRollbackException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Set; +import java.util.*; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; @@ -298,7 +298,8 @@ public T startTransaction(TransactionLogic logic, TransactionIsolationLev tries++; try { return startTransactionHelper(logic, isolationLevel); - } catch (SQLException | StorageQueryException | StorageTransactionLogicException e) { + } catch (SQLException | StorageQueryException | StorageTransactionLogicException | + TenantOrAppNotFoundException e) { Throwable actualException = e; if (e instanceof StorageQueryException) { actualException = e.getCause(); @@ -358,6 +359,8 @@ public T startTransaction(TransactionLogic logic, TransactionIsolationLev throw (StorageQueryException) e; } else if (e instanceof StorageTransactionLogicException) { throw (StorageTransactionLogicException) e; + } else if (e instanceof TenantOrAppNotFoundException) { + throw new StorageTransactionLogicException(e); } throw new StorageQueryException(e); } @@ -365,7 +368,7 @@ public T startTransaction(TransactionLogic logic, TransactionIsolationLev } protected T startTransactionHelper(TransactionLogic logic, TransactionIsolationLevel isolationLevel) - throws StorageQueryException, StorageTransactionLogicException, SQLException { + throws StorageQueryException, StorageTransactionLogicException, SQLException, TenantOrAppNotFoundException { // TODO here something is fucked up Connection con = null; Integer defaultTransactionIsolation = null; try { @@ -968,6 +971,13 @@ public AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, S } } + @Override + public void signUpMultiple(List users) + throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, + TenantOrAppNotFoundException, StorageTransactionLogicException { + EmailPasswordQueries.signUpMultiple(this, users); + } + @Override public void deleteEmailPasswordUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, boolean deleteUserIdMappingToo) @@ -1145,6 +1155,35 @@ public void updateIsEmailVerified_Transaction(AppIdentifier appIdentifier, Trans } } + @Override + public void updateMultipleIsEmailVerified_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + Map emailToUserId, boolean isEmailVerified) + throws StorageQueryException, TenantOrAppNotFoundException { + Connection sqlCon = (Connection) con.getConnection(); + try { + EmailVerificationQueries.updateMultipleUsersIsEmailVerified_Transaction(this, sqlCon, appIdentifier, + emailToUserId, isEmailVerified); + } catch (SQLException e) { + if (e instanceof PSQLException) { + PostgreSQLConfig config = Config.getConfig(this); + ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); + + if (isForeignKeyConstraintError(serverMessage, config.getEmailVerificationTable(), "app_id")) { + throw new TenantOrAppNotFoundException(appIdentifier); + } + } + + boolean isPSQLPrimKeyError = e instanceof PSQLException && isPrimaryKeyError( + ((PSQLException) e).getServerErrorMessage(), + Config.getConfig(this).getEmailVerificationTable()); + + if (!isEmailVerified || !isPSQLPrimKeyError) { + throw new StorageQueryException(e); + } + // we do not throw an error since the email is already verified + } + } + @Override public void deleteEmailVerificationUserInfo_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) @@ -1329,6 +1368,19 @@ public void deleteThirdPartyUser_Transaction(TransactionConnection con, AppIdent } } + @Override + public void importThirdPartyUsers_Transaction(TransactionConnection con, + Collection usersToImport) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + ThirdPartyQueries.importUser_Transaction(this, sqlCon, usersToImport); + } catch (SQLException e) { + e.printStackTrace(System.out); + throw new StorageQueryException(e); + } + } + @Override public long getUsersCount(TenantIdentifier tenantIdentifier, RECIPE_ID[] includeRecipeIds) throws StorageQueryException { @@ -1847,6 +1899,18 @@ public void deletePasswordlessUser_Transaction(TransactionConnection con, AppIde } } + @Override + public void importPasswordlessUsers_Transaction(TransactionConnection con, + Collection users) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + PasswordlessQueries.importUsers_Transaction(sqlCon, this, users); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public PasswordlessDevice getDevice(TenantIdentifier tenantIdentifier, String deviceIdHash) throws StorageQueryException { @@ -1939,6 +2003,18 @@ public JsonObject getUserMetadata_Transaction(AppIdentifier appIdentifier, Trans } } + @Override + public Map getMultipleUsersMetadatas_Transaction(AppIdentifier appIdentifier, TransactionConnection + con, List userIds) + throws StorageQueryException { + Connection sqlCon = (Connection) con.getConnection(); + try { + return UserMetadataQueries.getMultipleUsersMetadatas_Transaction(this, sqlCon, appIdentifier, userIds); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, JsonObject metadata) @@ -1960,6 +2036,26 @@ public int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionC } } + @Override + public void setMultipleUsersMetadatas_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + Map metadataByUserId) + throws StorageQueryException, TenantOrAppNotFoundException { + Connection sqlCon = (Connection) con.getConnection(); + try { + UserMetadataQueries.setMultipleUsersMetadatas_Transaction(this, sqlCon, appIdentifier, metadataByUserId); + } catch (SQLException e) { + if (e instanceof PSQLException) { + PostgreSQLConfig config = Config.getConfig(this); + ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); + + if (isForeignKeyConstraintError(serverMessage, config.getUserMetadataTable(), "app_id")) { + throw new TenantOrAppNotFoundException(appIdentifier); + } + } + throw new StorageQueryException(e); + } + } + @Override public int deleteUserMetadata_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException { @@ -2000,6 +2096,17 @@ public void addRoleToUser(TenantIdentifier tenantIdentifier, String userId, Stri } throw new StorageQueryException(e); } + } + + @Override + public void addRolesToUsers_Transaction(TransactionConnection connection, + Map> rolesToUserByTenants) + throws StorageQueryException { + try { + UserRolesQueries.addRolesToUsers_Transaction(this, (Connection) connection.getConnection(), rolesToUserByTenants); + } catch (SQLException e) { + throw new StorageQueryException(e); + } } @@ -2746,6 +2853,26 @@ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentif } } + @Override + public void createDevices_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + List devices) + throws StorageQueryException, TenantOrAppNotFoundException { + Connection sqlCon = (Connection) con.getConnection(); + try { + TOTPQueries.createDevices_Transaction(this, sqlCon, appIdentifier, devices); + } catch (SQLException e) { + Exception actualException = e; + + if (actualException instanceof PSQLException) { + ServerErrorMessage errMsg = ((PSQLException) actualException).getServerErrorMessage(); + if (isForeignKeyConstraintError(errMsg, Config.getConfig(this).getTotpUsersTable(), "app_id")) { + throw new TenantOrAppNotFoundException(appIdentifier); + } + } + throw new StorageQueryException(e); + } + } + @Override public TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, String deviceName) throws StorageQueryException { @@ -2945,6 +3072,18 @@ public AuthRecipeUserInfo getPrimaryUserById_Transaction(AppIdentifier appIdenti } } + @Override + public List getPrimaryUsersByIds_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + List userIds) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return GeneralQueries.getPrimaryUserInfosForUserIds_Transaction(this, sqlCon, appIdentifier, userIds); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String email) @@ -3012,6 +3151,17 @@ public void makePrimaryUser_Transaction(AppIdentifier appIdentifier, Transaction } } + @Override + public void makePrimaryUsers_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + List userIds) throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + GeneralQueries.makePrimaryUsers_Transaction(this, sqlCon, appIdentifier, userIds); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public void linkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String recipeUserId, String primaryUserId) throws StorageQueryException { @@ -3025,6 +3175,19 @@ public void linkAccounts_Transaction(AppIdentifier appIdentifier, TransactionCon } } + @Override + public void linkMultipleAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + Map recipeUserIdByPrimaryUserId) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + GeneralQueries.linkMultipleAccounts_Transaction(this, sqlCon, appIdentifier, recipeUserIdByPrimaryUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + + } + @Override public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, String recipeUserId) diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java index 0a01fd4f..27a993d8 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java @@ -16,18 +16,6 @@ package io.supertokens.storage.postgresql.queries; -import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; -import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import io.supertokens.pluginInterface.RowMapper; import io.supertokens.pluginInterface.bulkimport.BulkImportStorage.BULK_IMPORT_USER_STATUS; import io.supertokens.pluginInterface.bulkimport.BulkImportUser; @@ -38,6 +26,17 @@ import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; + public class BulkImportQueries { static String getQueryToCreateBulkImportUsersTable(Start start) { String schema = Config.getConfig(start).getTableSchema(); @@ -222,6 +221,7 @@ public static List getBulkImportUsers(Start start, AppIdentifier public static List deleteBulkImportUsers(Start start, AppIdentifier appIdentifier, @Nonnull String[] bulkImportUserIds) throws SQLException, StorageQueryException { + System.out.println("Deleting bulkimportuser ids: " + bulkImportUserIds.length); if (bulkImportUserIds.length == 0) { return new ArrayList<>(); } 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 efed6c7f..90626511 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java @@ -19,19 +19,19 @@ import io.supertokens.pluginInterface.RowMapper; import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.authRecipe.LoginMethod; +import io.supertokens.pluginInterface.emailpassword.EmailPasswordImportUser; import io.supertokens.pluginInterface.emailpassword.PasswordResetTokenInfo; -import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -import io.supertokens.storage.postgresql.ConnectionPool; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; @@ -336,6 +336,96 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden }); } + public static void signUpMultiple(Start start, List usersToSignUp) + throws StorageQueryException, StorageTransactionLogicException { + start.startTransaction(con -> { + Connection sqlCon = (Connection) con.getConnection(); + try { + String app_id_to_user_id_QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() + + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; + + String all_auth_recipe_users_QUERY = "INSERT INTO " + getConfig(start).getUsersTable() + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + + " VALUES(?, ?, ?, ?, ?, ?, ?)"; + + String emailpassword_users_QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUsersTable() + + "(app_id, user_id, email, password_hash, time_joined)" + " VALUES(?, ?, ?, ?, ?)"; + + String emailpassword_users_to_tenant_QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUserToTenantTable() + + "(app_id, tenant_id, user_id, email)" + " VALUES(?, ?, ?, ?)"; + + PreparedStatement appIdToUserId = sqlCon.prepareStatement(app_id_to_user_id_QUERY); + PreparedStatement allAuthRecipeUsers = sqlCon.prepareStatement(all_auth_recipe_users_QUERY); + PreparedStatement emailPasswordUsers = sqlCon.prepareStatement(emailpassword_users_QUERY); + PreparedStatement emailPasswordUsersToTenant = sqlCon.prepareStatement(emailpassword_users_to_tenant_QUERY); + + int counter = 0; + for(EmailPasswordImportUser user: usersToSignUp) { + String userId = user.userId; + TenantIdentifier tenantIdentifier = user.tenantIdentifier; + + appIdToUserId.setString(1, tenantIdentifier.getAppId()); + appIdToUserId.setString(2, userId); + appIdToUserId.setString(3, userId); + appIdToUserId.setString(4, EMAIL_PASSWORD.toString()); + appIdToUserId.addBatch(); + + + allAuthRecipeUsers.setString(1, tenantIdentifier.getAppId()); + allAuthRecipeUsers.setString(2, tenantIdentifier.getTenantId()); + allAuthRecipeUsers.setString(3, userId); + allAuthRecipeUsers.setString(4, userId); + allAuthRecipeUsers.setString(5, EMAIL_PASSWORD.toString()); + allAuthRecipeUsers.setLong(6, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsers.setLong(7, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsers.addBatch(); + + emailPasswordUsers.setString(1, tenantIdentifier.getAppId()); + emailPasswordUsers.setString(2, userId); + emailPasswordUsers.setString(3, user.email); + emailPasswordUsers.setString(4, user.passwordHash); + emailPasswordUsers.setLong(5, user.timeJoinedMSSinceEpoch); + emailPasswordUsers.addBatch(); + + emailPasswordUsersToTenant.setString(1, tenantIdentifier.getAppId()); + emailPasswordUsersToTenant.setString(2, tenantIdentifier.getTenantId()); + emailPasswordUsersToTenant.setString(3, userId); + emailPasswordUsersToTenant.setString(4, user.email); + emailPasswordUsersToTenant.addBatch(); + counter++; + if(counter % 100 == 0) { + appIdToUserId.executeBatch(); + allAuthRecipeUsers.executeBatch(); + emailPasswordUsers.executeBatch(); + emailPasswordUsersToTenant.executeBatch(); + } + } + + //execute the remaining ones + appIdToUserId.executeBatch(); + allAuthRecipeUsers.executeBatch(); + emailPasswordUsers.executeBatch(); + emailPasswordUsersToTenant.executeBatch(); + + //UserInfoPartial userInfo = new UserInfoPartial(userId, email, passwordHash, timeJoined); +// fillUserInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); +// fillUserInfoWithVerified_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); + sqlCon.commit(); + //return AuthRecipeUserInfo.create(userId, false, userInfo.toLoginMethod()); + } catch (SQLException throwables) { + throwables.printStackTrace(System.out); + SQLException next = throwables.getNextException(); + while(next != null) { + next.printStackTrace(System.out); + next = next.getNextException(); + } + throw new StorageTransactionLogicException(throwables); + } + return null; + }); + } + public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, String userId, boolean deleteUserIdMappingToo) throws StorageQueryException, SQLException { 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 9c70cf8f..1771608a 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java @@ -28,6 +28,7 @@ import io.supertokens.storage.postgresql.utils.Utils; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; @@ -124,6 +125,48 @@ public static void updateUsersIsEmailVerified_Transaction(Start start, Connectio } } + public static void updateMultipleUsersIsEmailVerified_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + Map emailToUserIds, + boolean isEmailVerified) + throws SQLException, StorageQueryException { + + if (isEmailVerified) { + String QUERY = "INSERT INTO " + getConfig(start).getEmailVerificationTable() + + "(app_id, user_id, email) VALUES(?, ?, ?)"; + PreparedStatement insertQuery = con.prepareStatement(QUERY); + int counter = 0; + for(Map.Entry emailToUser : emailToUserIds.entrySet()){ + insertQuery.setString(1, appIdentifier.getAppId()); + insertQuery.setString(2, emailToUser.getValue()); + insertQuery.setString(3, emailToUser.getKey()); + insertQuery.addBatch(); + + counter++; + if (counter % 100 == 0) { + insertQuery.executeBatch(); + } + } + insertQuery.executeBatch(); + } else { + String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTable() + + " WHERE app_id = ? AND user_id = ? AND email = ?"; + PreparedStatement deleteQuery = con.prepareStatement(QUERY); + int counter = 0; + for (Map.Entry emailToUser : emailToUserIds.entrySet()) { + deleteQuery.setString(1, appIdentifier.getAppId()); + deleteQuery.setString(2, emailToUser.getValue()); + deleteQuery.setString(3, emailToUser.getKey()); + deleteQuery.addBatch(); + + counter++; + if (counter % 100 == 0) { + deleteQuery.executeBatch(); + } + } + deleteQuery.executeBatch(); + } + } + public static void deleteAllEmailVerificationTokensForUser_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String userId, 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 8ac6aa8c..b826fbe5 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -34,6 +34,7 @@ import org.jetbrains.annotations.TestOnly; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; @@ -1220,6 +1221,37 @@ public static void makePrimaryUser_Transaction(Start start, Connection sqlCon, A } } + public static void makePrimaryUsers_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + List userIds) + throws SQLException, StorageQueryException { + + String users_update_QUERY = "UPDATE " + getConfig(start).getUsersTable() + + " SET is_linked_or_is_a_primary_user = true WHERE app_id = ? AND user_id = ?"; + String appid_to_userid_update_QUERY = "UPDATE " + getConfig(start).getAppIdToUserIdTable() + + " SET is_linked_or_is_a_primary_user = true WHERE app_id = ? AND user_id = ?"; + + PreparedStatement usersUpdateStatement = sqlCon.prepareStatement(users_update_QUERY); + PreparedStatement appIdToUserIdUpdateStatement = sqlCon.prepareStatement(appid_to_userid_update_QUERY); + int counter = 0; + for(String userId: userIds){ + usersUpdateStatement.setString(1, appIdentifier.getAppId()); + usersUpdateStatement.setString(2, userId); + usersUpdateStatement.addBatch(); + + appIdToUserIdUpdateStatement.setString(1, appIdentifier.getAppId()); + appIdToUserIdUpdateStatement.setString(2, userId); + appIdToUserIdUpdateStatement.addBatch(); + + counter++; + if(counter % 100 == 0) { + usersUpdateStatement.executeBatch(); + appIdToUserIdUpdateStatement.executeBatch(); + } + } + usersUpdateStatement.executeBatch(); + appIdToUserIdUpdateStatement.executeBatch(); + } + public static void linkAccounts_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String recipeUserId, String primaryUserId) throws SQLException, StorageQueryException { @@ -1250,6 +1282,73 @@ public static void linkAccounts_Transaction(Start start, Connection sqlCon, AppI } } + public static void linkMultipleAccounts_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + Map recipeUserIdToPrimaryUserId) + throws SQLException, StorageQueryException { + + if(recipeUserIdToPrimaryUserId == null || recipeUserIdToPrimaryUserId.isEmpty()){ + return; + } + + String update_users_QUERY = "UPDATE " + getConfig(start).getUsersTable() + + " SET is_linked_or_is_a_primary_user = true, primary_or_recipe_user_id = ? WHERE app_id = ? AND " + + "user_id = ?"; + + String update_appid_to_userid_QUERY = "UPDATE " + getConfig(start).getAppIdToUserIdTable() + + " SET is_linked_or_is_a_primary_user = true, primary_or_recipe_user_id = ? WHERE app_id = ? AND " + + "user_id = ?"; + + PreparedStatement updateUsers = sqlCon.prepareStatement(update_users_QUERY); + PreparedStatement updateAppIdToUserId = sqlCon.prepareStatement(update_appid_to_userid_QUERY); + + int counter = 0; + for(Map.Entry linkEntry : recipeUserIdToPrimaryUserId.entrySet()) { + String primaryUserId = linkEntry.getValue(); + String recipeUserId = linkEntry.getKey(); + + updateUsers.setString(1, primaryUserId); + updateUsers.setString(2, appIdentifier.getAppId()); + updateUsers.setString(3, recipeUserId); + updateUsers.addBatch(); + + updateAppIdToUserId.setString(1, primaryUserId); + updateAppIdToUserId.setString(2, appIdentifier.getAppId()); + updateAppIdToUserId.setString(3, recipeUserId); + updateAppIdToUserId.addBatch(); + + counter++; + if (counter % 100 == 0) { + updateUsers.executeBatch(); + updateAppIdToUserId.executeBatch(); + } + } + + updateUsers.executeBatch(); + updateAppIdToUserId.executeBatch(); + + updateTimeJoinedForPrimaryUsers_Transaction(start, sqlCon, appIdentifier, + new ArrayList<>(recipeUserIdToPrimaryUserId.values())); + } + + public static void updateTimeJoinedForPrimaryUsers_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, List primaryUserIds) + throws SQLException, StorageQueryException { + String QUERY = "UPDATE " + getConfig(start).getUsersTable() + + " SET primary_or_recipe_user_time_joined = (SELECT MIN(time_joined) FROM " + + getConfig(start).getUsersTable() + " WHERE app_id = ? AND primary_or_recipe_user_id = ?) WHERE " + + " app_id = ? AND primary_or_recipe_user_id = ?"; + PreparedStatement updateStatement = sqlCon.prepareStatement(QUERY); + for(String primaryUserId : primaryUserIds) { + updateStatement.setString(1, appIdentifier.getAppId()); + updateStatement.setString(2, primaryUserId); + updateStatement.setString(3, appIdentifier.getAppId()); + updateStatement.setString(4, primaryUserId); + updateStatement.addBatch(); + } + + updateStatement.executeBatch(); + } + public static void unlinkAccounts_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String primaryUserId, String recipeUserId) throws SQLException, StorageQueryException { @@ -1481,6 +1580,17 @@ public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start s return result.get(0); } + public static List getPrimaryUserInfosForUserIds_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, List ids) + throws SQLException, StorageQueryException { + + List result = getPrimaryUserInfoForUserIds_Transaction(start, con, appIdentifier, ids); + if (result.isEmpty()) { + return null; + } + return result; + } + private static List getPrimaryUserInfoForUserIds(Start start, AppIdentifier appIdentifier, List userIds) 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 bfe1a2a9..eed57c31 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java @@ -26,6 +26,7 @@ import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; +import io.supertokens.pluginInterface.passwordless.PasswordlessImportUser; import io.supertokens.pluginInterface.sqlStorage.SQLStorage.TransactionIsolationLevel; import io.supertokens.storage.postgresql.ConnectionPool; import io.supertokens.storage.postgresql.Start; @@ -35,6 +36,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; @@ -1110,6 +1112,76 @@ private static List fillUserInfoWithTenantIds(Start start, return userInfos; } + public static void importUsers_Transaction(Connection sqlCon, Start start, + Collection users) throws SQLException { + + String app_id_to_user_id_QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() + + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; + PreparedStatement appIdToUserIdStatement = sqlCon.prepareStatement(app_id_to_user_id_QUERY); + + String all_auth_recipe_users_QUERY = "INSERT INTO " + getConfig(start).getUsersTable() + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + + " VALUES(?, ?, ?, ?, ?, ?, ?)"; + PreparedStatement allAuthRecipeUsersStatement = sqlCon.prepareStatement(all_auth_recipe_users_QUERY); + + String passwordless_users_QUERY = "INSERT INTO " + getConfig(start).getPasswordlessUsersTable() + + "(app_id, user_id, email, phone_number, time_joined)" + " VALUES(?, ?, ?, ?, ?)"; + PreparedStatement passwordlessUsersStatement = sqlCon.prepareStatement(passwordless_users_QUERY); + + String passwordless_user_to_tenant_QUERY = "INSERT INTO " + getConfig(start).getPasswordlessUserToTenantTable() + + "(app_id, tenant_id, user_id, email, phone_number)" + " VALUES(?, ?, ?, ?, ?)"; + PreparedStatement passwordlessUserToTenantStatement = sqlCon.prepareStatement(passwordless_user_to_tenant_QUERY); + + int counter = 0; + for (PasswordlessImportUser user: users){ + TenantIdentifier tenantIdentifier = user.tenantIdentifier; + appIdToUserIdStatement.setString(1, tenantIdentifier.getAppId()); + appIdToUserIdStatement.setString(2, user.userId); + appIdToUserIdStatement.setString(3, user.userId); + appIdToUserIdStatement.setString(4, PASSWORDLESS.toString()); + appIdToUserIdStatement.addBatch(); + + allAuthRecipeUsersStatement.setString(1, tenantIdentifier.getAppId()); + allAuthRecipeUsersStatement.setString(2, tenantIdentifier.getTenantId()); + allAuthRecipeUsersStatement.setString(3, user.userId); + allAuthRecipeUsersStatement.setString(4, user.userId); + allAuthRecipeUsersStatement.setString(5, PASSWORDLESS.toString()); + allAuthRecipeUsersStatement.setLong(6, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsersStatement.setLong(7, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsersStatement.addBatch(); + + passwordlessUsersStatement.setString(1, tenantIdentifier.getAppId()); + passwordlessUsersStatement.setString(2, user.userId); + passwordlessUsersStatement.setString(3, user.email); + passwordlessUsersStatement.setString(4, user.phoneNumber); + passwordlessUsersStatement.setLong(5, user.timeJoinedMSSinceEpoch); + passwordlessUsersStatement.addBatch(); + + passwordlessUserToTenantStatement.setString(1, tenantIdentifier.getAppId()); + passwordlessUserToTenantStatement.setString(2, tenantIdentifier.getTenantId()); + passwordlessUserToTenantStatement.setString(3, user.userId); + passwordlessUserToTenantStatement.setString(4, user.email); + passwordlessUserToTenantStatement.setString(5, user.phoneNumber); + passwordlessUserToTenantStatement.addBatch(); + + counter++; + + if(counter % 100 == 0) { + appIdToUserIdStatement.executeBatch(); + allAuthRecipeUsersStatement.executeBatch(); + passwordlessUsersStatement.executeBatch(); + passwordlessUserToTenantStatement.executeBatch(); + } + } + + appIdToUserIdStatement.executeBatch(); + allAuthRecipeUsersStatement.executeBatch(); + passwordlessUsersStatement.executeBatch(); + passwordlessUserToTenantStatement.executeBatch(); + + } + private static class PasswordlessDeviceRowMapper implements RowMapper { private static final PasswordlessDeviceRowMapper INSTANCE = new PasswordlessDeviceRowMapper(); 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 60270a65..a364da86 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java @@ -1,22 +1,22 @@ package io.supertokens.storage.postgresql.queries; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import io.supertokens.pluginInterface.multitenancy.AppIdentifier; -import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -import io.supertokens.storage.postgresql.Start; -import io.supertokens.storage.postgresql.config.Config; import io.supertokens.pluginInterface.RowMapper; import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.totp.TOTPDevice; import io.supertokens.pluginInterface.totp.TOTPUsedCode; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -146,6 +146,52 @@ public static void createDevice_Transaction(Start start, Connection sqlCon, AppI insertDevice_Transaction(start, sqlCon, appIdentifier, device); } + public static void createDevices_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + List devices) + throws SQLException, StorageQueryException { + + String insert_user_QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUsersTable() + + " (app_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING"; + + String insert_device_QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUserDevicesTable() + + + " (app_id, user_id, device_name, secret_key, period, skew, verified, created_at) VALUES (?, ?, ?, ?, " + + "?, ?, ?, ?) ON CONFLICT (app_id, user_id, device_name) DO UPDATE SET secret_key = ?, period = ?, skew = ?, created_at = ?, verified = ?"; + + PreparedStatement insertUserStatement = sqlCon.prepareStatement(insert_user_QUERY); + PreparedStatement insertDeviceStatement = sqlCon.prepareStatement(insert_device_QUERY); + + int counter = 0; + for(TOTPDevice device : devices){ + insertUserStatement.setString(1, appIdentifier.getAppId()); + insertUserStatement.setString(2, device.userId); + insertUserStatement.addBatch(); + + insertDeviceStatement.setString(1, appIdentifier.getAppId()); + insertDeviceStatement.setString(2, device.userId); + insertDeviceStatement.setString(3, device.deviceName); + insertDeviceStatement.setString(4, device.secretKey); + insertDeviceStatement.setInt(5, device.period); + insertDeviceStatement.setInt(6, device.skew); + insertDeviceStatement.setBoolean(7, device.verified); + insertDeviceStatement.setLong(8, device.createdAt); + insertDeviceStatement.setString(9, device.secretKey); + insertDeviceStatement.setInt(10, device.period); + insertDeviceStatement.setInt(11, device.skew); + insertDeviceStatement.setLong(12, device.createdAt); + insertDeviceStatement.setBoolean(13, device.verified); + insertDeviceStatement.addBatch(); + counter++; + if(counter % 100 == 0) { + insertUserStatement.executeBatch(); + insertDeviceStatement.executeBatch(); + } + } + + insertUserStatement.executeBatch(); + insertDeviceStatement.executeBatch(); + } + public static TOTPDevice getDeviceByName_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId, String deviceName) throws SQLException, StorageQueryException { 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 964b53cd..6c8be437 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java @@ -19,19 +19,19 @@ import io.supertokens.pluginInterface.RowMapper; import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.authRecipe.LoginMethod; -import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException; +import io.supertokens.pluginInterface.thirdparty.ThirdPartyImportUser; import io.supertokens.storage.postgresql.ConnectionPool; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; @@ -527,6 +527,80 @@ public static boolean removeUserIdFromTenant_Transaction(Start start, Connection // automatically deleted from thirdparty_user_to_tenant because of foreign key constraint } + public static void importUser_Transaction(Start start, Connection sqlConnection, Collection users) + throws SQLException { + + String app_id_userid_QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() + + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; + + String all_auth_recipe_users_QUERY = "INSERT INTO " + getConfig(start).getUsersTable() + + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + + " VALUES(?, ?, ?, ?, ?, ?, ?)"; + + String thirdparty_users_QUERY = "INSERT INTO " + getConfig(start).getThirdPartyUsersTable() + + "(app_id, third_party_id, third_party_user_id, user_id, email, time_joined)" + + " VALUES(?, ?, ?, ?, ?, ?)"; + + String thirdparty_user_to_tenant_QUERY = "INSERT INTO " + getConfig(start).getThirdPartyUserToTenantTable() + + "(app_id, tenant_id, user_id, third_party_id, third_party_user_id)" + + " VALUES(?, ?, ?, ?, ?)"; + + PreparedStatement appIdToUserIdStatement = sqlConnection.prepareStatement(app_id_userid_QUERY); + PreparedStatement allAuthRecipeUsersStatement = sqlConnection.prepareStatement(all_auth_recipe_users_QUERY); + PreparedStatement thirdPartyUsersStatement = sqlConnection.prepareStatement(thirdparty_users_QUERY); + PreparedStatement thirdPartyUsersToTenantStatement = sqlConnection.prepareStatement( + thirdparty_user_to_tenant_QUERY); + + int counter = 0; + for (ThirdPartyImportUser user : users) { + TenantIdentifier tenantIdentifier = user.tenantIdentifier; + appIdToUserIdStatement.setString(1, tenantIdentifier.getAppId()); + appIdToUserIdStatement.setString(2, user.userId); + appIdToUserIdStatement.setString(3, user.userId); + appIdToUserIdStatement.setString(4, THIRD_PARTY.toString()); + appIdToUserIdStatement.addBatch(); + + allAuthRecipeUsersStatement.setString(1, tenantIdentifier.getAppId()); + allAuthRecipeUsersStatement.setString(2, tenantIdentifier.getTenantId()); + allAuthRecipeUsersStatement.setString(3, user.userId); + allAuthRecipeUsersStatement.setString(4, user.userId); + allAuthRecipeUsersStatement.setString(5, THIRD_PARTY.toString()); + allAuthRecipeUsersStatement.setLong(6, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsersStatement.setLong(7, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsersStatement.addBatch(); + + thirdPartyUsersStatement.setString(1, tenantIdentifier.getAppId()); + thirdPartyUsersStatement.setString(2, user.thirdpartyId); + thirdPartyUsersStatement.setString(3, user.thirdpartyUserId); + thirdPartyUsersStatement.setString(4, user.userId); + thirdPartyUsersStatement.setString(5, user.email); + thirdPartyUsersStatement.setLong(6, user.timeJoinedMSSinceEpoch); + thirdPartyUsersStatement.addBatch(); + + thirdPartyUsersToTenantStatement.setString(1, tenantIdentifier.getAppId()); + thirdPartyUsersToTenantStatement.setString(2, tenantIdentifier.getTenantId()); + thirdPartyUsersToTenantStatement.setString(3, user.userId); + thirdPartyUsersToTenantStatement.setString(4, user.thirdpartyId); + thirdPartyUsersToTenantStatement.setString(5, user.thirdpartyUserId); + thirdPartyUsersToTenantStatement.addBatch(); + + counter++; + if(counter % 100 == 0) { + appIdToUserIdStatement.executeBatch(); + allAuthRecipeUsersStatement.executeBatch(); + thirdPartyUsersStatement.executeBatch(); + thirdPartyUsersToTenantStatement.executeBatch(); + } + } + + appIdToUserIdStatement.executeBatch(); + allAuthRecipeUsersStatement.executeBatch(); + thirdPartyUsersStatement.executeBatch(); + thirdPartyUsersToTenantStatement.executeBatch(); + } + private static UserInfoPartial fillUserInfoWithVerified_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) 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..cd928196 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java @@ -25,7 +25,11 @@ import io.supertokens.storage.postgresql.utils.Utils; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -93,6 +97,31 @@ public static int setUserMetadata_Transaction(Start start, Connection con, AppId }); } + public static void setMultipleUsersMetadatas_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + Map metadatasByUserId) + throws SQLException, StorageQueryException { + + String QUERY = "INSERT INTO " + getConfig(start).getUserMetadataTable() + + "(app_id, user_id, user_metadata) VALUES(?, ?, ?) " + + "ON CONFLICT(app_id, user_id) DO UPDATE SET user_metadata=excluded.user_metadata;"; + PreparedStatement insertStatement = con.prepareStatement(QUERY); + + int counter = 0; + for(Map.Entry metadataByUserId : metadatasByUserId.entrySet()){ + insertStatement.setString(1, appIdentifier.getAppId()); + insertStatement.setString(2, metadataByUserId.getKey()); + insertStatement.setString(3, metadataByUserId.getValue().toString()); + insertStatement.addBatch(); + + counter++; + if(counter % 100 == 0) { + insertStatement.executeBatch(); + } + } + + insertStatement.executeBatch(); + } + public static JsonObject getUserMetadata_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { @@ -110,6 +139,28 @@ public static JsonObject getUserMetadata_Transaction(Start start, Connection con }); } + public static Map getMultipleUsersMetadatas_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + List userIds) + throws SQLException, StorageQueryException { + String QUERY = "SELECT user_id, user_metadata FROM " + getConfig(start).getUserMetadataTable() + + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") FOR UPDATE"; + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i< userIds.size(); i++){ + pst.setString(2+i, userIds.get(i)); + } + }, result -> { + Map userMetadataByUserId = new HashMap<>(); + JsonParser jp = new JsonParser(); + if (result.next()) { + userMetadataByUserId.put(result.getString("user_id"), + jp.parse(result.getString("user_metadata")).getAsJsonObject()); + } + return userMetadataByUserId; + }); + } + public static JsonObject getUserMetadata(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT user_metadata 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 5825164c..212ba35d 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java @@ -17,18 +17,18 @@ package io.supertokens.storage.postgresql.queries; import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -202,6 +202,32 @@ public static int addRoleToUser(Start start, TenantIdentifier tenantIdentifier, }); } + public static void addRolesToUsers_Transaction(Start start, Connection connection, Map> rolesToUserByTenants) //tenant -> user -> role + throws SQLException, StorageQueryException { + String QUERY = "INSERT INTO " + getConfig(start).getUserRolesTable() + + "(app_id, tenant_id, user_id, role) VALUES(?, ?, ?, ?);"; + PreparedStatement insertStatement = connection.prepareStatement(QUERY); + + int counter = 0; + for(Map.Entry> tenantsEntry : rolesToUserByTenants.entrySet()) { + for(Map.Entry rolesToUser : tenantsEntry.getValue().entrySet()) { + + insertStatement.setString(1, tenantsEntry.getKey().getAppId()); + insertStatement.setString(2, tenantsEntry.getKey().getTenantId()); + insertStatement.setString(3, rolesToUser.getKey()); + insertStatement.setString(4, rolesToUser.getValue()); + insertStatement.addBatch(); + counter++; + + if(counter % 100 == 0) { + insertStatement.executeBatch(); + } + } + } + + insertStatement.executeBatch(); + } + public static String[] getRolesForUser(Start start, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { String QUERY = "SELECT role FROM " + getConfig(start).getUserRolesTable() From 11bd0676dbde6e85d88353fe2f75a2b69f90715b Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 22 Nov 2024 17:09:46 +0100 Subject: [PATCH 43/45] fix: fixes and error handling changes --- .../supertokens/storage/postgresql/Start.java | 309 ++++++++++++++++-- .../queries/ActiveUsersQueries.java | 30 +- .../postgresql/queries/BulkImportQueries.java | 30 +- .../queries/EmailPasswordQueries.java | 206 +++++++----- .../queries/EmailVerificationQueries.java | 85 +++++ .../postgresql/queries/GeneralQueries.java | 97 ++++-- .../queries/PasswordlessQueries.java | 102 ++++++ .../postgresql/queries/SessionQueries.java | 27 ++ .../postgresql/queries/TOTPQueries.java | 26 ++ .../postgresql/queries/ThirdPartyQueries.java | 118 +++++++ .../queries/UserIdMappingQueries.java | 71 ++++ .../queries/UserMetadataQueries.java | 8 + .../postgresql/queries/UserRolesQueries.java | 48 +++ 13 files changed, 1020 insertions(+), 137 deletions(-) diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index faa4f4aa..59718e6a 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -27,6 +27,7 @@ import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage; import io.supertokens.pluginInterface.bulkimport.BulkImportStorage; import io.supertokens.pluginInterface.bulkimport.BulkImportUser; +import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportBatchInsertException; import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportTransactionRolledBackException; import io.supertokens.pluginInterface.bulkimport.sqlStorage.BulkImportSQLStorage; import io.supertokens.pluginInterface.dashboard.DashboardSearchTags; @@ -110,6 +111,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLTransactionRollbackException; @@ -368,7 +370,7 @@ public T startTransaction(TransactionLogic logic, TransactionIsolationLev } protected T startTransactionHelper(TransactionLogic logic, TransactionIsolationLevel isolationLevel) - throws StorageQueryException, StorageTransactionLogicException, SQLException, TenantOrAppNotFoundException { // TODO here something is fucked up + throws StorageQueryException, StorageTransactionLogicException, SQLException, TenantOrAppNotFoundException { Connection con = null; Integer defaultTransactionIsolation = null; try { @@ -808,13 +810,6 @@ public boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, Str } catch (SQLException e) { throw new StorageQueryException(e); } - } else if (className.equals(TOTPStorage.class.getName())) { - try { - TOTPDevice[] devices = TOTPQueries.getDevices(this, appIdentifier, userId); - return devices.length > 0; - } catch (SQLException e) { - throw new StorageQueryException(e); - } } else if (className.equals(JWTRecipeStorage.class.getName())) { return false; } else if (className.equals(ActiveUsersStorage.class.getName())) { @@ -824,6 +819,73 @@ public boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, Str } } + @Override + public Map> findNonAuthRecipesWhereForUserIdsUsed(AppIdentifier appIdentifier, + List userIds) + throws StorageQueryException { + try { + Map> sessionHandlesByUserId = SessionQueries.getAllNonExpiredSessionHandlesForUsers(this, appIdentifier, userIds); + Map> rolesByUserIds = UserRolesQueries.getRolesForUsers(this, appIdentifier, userIds); + Map userMetadatasByIds = UserMetadataQueries.getMultipleUserMetadatas(this, appIdentifier, userIds); + Set userIdsUsedInEmailVerification = EmailVerificationQueries.findUserIdsBeingUsedForEmailVerification(this, appIdentifier, userIds); + Map> devicesByUserIds = TOTPQueries.getDevicesForMultipleUsers(this, appIdentifier, userIds); + Map lastActivesByUserIds = ActiveUsersQueries.getLastActiveByMultipleUserIds(this, appIdentifier, userIds); + + Map> nonAuthRecipeClassnamesByUserIds = new HashMap<>(); + //session recipe + for(String userId: sessionHandlesByUserId.keySet()){ + if(!nonAuthRecipeClassnamesByUserIds.containsKey(userId)){ + nonAuthRecipeClassnamesByUserIds.put(userId, new ArrayList<>()); + } + nonAuthRecipeClassnamesByUserIds.get(userId).add(SessionStorage.class.getName()); + } + + //role recipe + for(String userId: rolesByUserIds.keySet()){ + if(!nonAuthRecipeClassnamesByUserIds.containsKey(userId)){ + nonAuthRecipeClassnamesByUserIds.put(userId, new ArrayList<>()); + } + nonAuthRecipeClassnamesByUserIds.get(userId).add(UserRolesStorage.class.getName()); + } + + //usermetadata recipe + for(String userId: userMetadatasByIds.keySet()){ + if(!nonAuthRecipeClassnamesByUserIds.containsKey(userId)){ + nonAuthRecipeClassnamesByUserIds.put(userId, new ArrayList<>()); + } + nonAuthRecipeClassnamesByUserIds.get(userId).add(UserMetadataStorage.class.getName()); + } + + //emailverification recipe + for(String userId: userIdsUsedInEmailVerification){ + if(!nonAuthRecipeClassnamesByUserIds.containsKey(userId)){ + nonAuthRecipeClassnamesByUserIds.put(userId, new ArrayList<>()); + } + nonAuthRecipeClassnamesByUserIds.get(userId).add(EmailVerificationStorage.class.getName()); + } + + //totp recipe + for(String userId: devicesByUserIds.keySet()){ + if(!nonAuthRecipeClassnamesByUserIds.containsKey(userId)){ + nonAuthRecipeClassnamesByUserIds.put(userId, new ArrayList<>()); + } + nonAuthRecipeClassnamesByUserIds.get(userId).add(TOTPStorage.class.getName()); + } + + //active users + for(String userId: lastActivesByUserIds.keySet()){ + if(!nonAuthRecipeClassnamesByUserIds.containsKey(userId)){ + nonAuthRecipeClassnamesByUserIds.put(userId, new ArrayList<>()); + } + nonAuthRecipeClassnamesByUserIds.get(userId).add(ActiveUsersStorage.class.getName()); + } + + return nonAuthRecipeClassnamesByUserIds; + } catch (SQLException | StorageTransactionLogicException exc) { + throw new StorageQueryException(exc); + } + } + @TestOnly @Override public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifier, String className, String userId) @@ -972,10 +1034,53 @@ public AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, S } @Override - public void signUpMultiple(List users) - throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, - TenantOrAppNotFoundException, StorageTransactionLogicException { - EmailPasswordQueries.signUpMultiple(this, users); + public void signUpMultipleViaBulkImport_Transaction(TransactionConnection connection, List users) + throws StorageQueryException, StorageTransactionLogicException { + try { + Connection sqlConnection = (Connection) connection.getConnection(); + EmailPasswordQueries.signUpMultipleForBulkImport_Transaction(this, sqlConnection, users); + } catch (StorageQueryException | SQLException | StorageTransactionLogicException e) { + Throwable actual = e.getCause(); + if (actual instanceof BatchUpdateException) { + BatchUpdateException batchUpdateException = (BatchUpdateException) actual; + Map errorByPosition = new HashMap<>(); + SQLException nextException = batchUpdateException.getNextException(); + while (nextException != null) { + + if (nextException instanceof PSQLException) { + PostgreSQLConfig config = Config.getConfig(this); + ServerErrorMessage serverMessage = ((PSQLException) nextException).getServerErrorMessage(); + + int position = getErroneousEntryPosition(batchUpdateException); + if (isUniqueConstraintError(serverMessage, config.getEmailPasswordUserToTenantTable(), + "email")) { + + errorByPosition.put(users.get(position).userId, new DuplicateEmailException()); + + } else if (isPrimaryKeyError(serverMessage, config.getThirdPartyUsersTable()) + || isPrimaryKeyError(serverMessage, config.getUsersTable()) + || isPrimaryKeyError(serverMessage, config.getThirdPartyUserToTenantTable()) + || isPrimaryKeyError(serverMessage, config.getAppIdToUserIdTable())) { + errorByPosition.put(users.get(position).userId, + new io.supertokens.pluginInterface.thirdparty.exception.DuplicateUserIdException()); + } + + } + nextException = nextException.getNextException(); + } + throw new StorageTransactionLogicException(new BulkImportBatchInsertException("emailpassword errors", errorByPosition)); + } + throw new StorageQueryException(e); + } + } + + private static int getErroneousEntryPosition(BatchUpdateException batchUpdateException) { + String errorMessage = batchUpdateException.getMessage(); + String searchFor = "Batch entry "; + int searchForIndex = errorMessage.indexOf("Batch entry "); + String entryIndex = errorMessage.substring(searchForIndex + searchFor.length(), errorMessage.indexOf(" ", searchForIndex + searchFor.length())); + int position = Integer.parseInt(entryIndex); + return position; } @Override @@ -1291,6 +1396,13 @@ public void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, S externalUserId); } + @Override + public void updateMultipleIsEmailVerifiedToExternalUserIds(AppIdentifier appIdentifier, + Map supertokensUserIdToExternalUserId) + throws StorageQueryException { + EmailVerificationQueries.updateMultipleIsEmailVerifiedToExternalUserIds(this, appIdentifier, supertokensUserIdToExternalUserId); + } + @Override public void deleteExpiredPasswordResetTokens() throws StorageQueryException { try { @@ -1370,14 +1482,48 @@ public void deleteThirdPartyUser_Transaction(TransactionConnection con, AppIdent @Override public void importThirdPartyUsers_Transaction(TransactionConnection con, - Collection usersToImport) - throws StorageQueryException { + List usersToImport) + throws StorageQueryException, StorageTransactionLogicException, TenantOrAppNotFoundException { try { Connection sqlCon = (Connection) con.getConnection(); ThirdPartyQueries.importUser_Transaction(this, sqlCon, usersToImport); } catch (SQLException e) { - e.printStackTrace(System.out); - throw new StorageQueryException(e); + Throwable actual = e.getCause(); + if (actual instanceof BatchUpdateException) { + BatchUpdateException batchUpdateException = (BatchUpdateException) actual; + Map errorByPosition = new HashMap<>(); + SQLException nextException = batchUpdateException.getNextException(); + while (nextException != null) { + + if (nextException instanceof PSQLException) { + PostgreSQLConfig config = Config.getConfig(this); + ServerErrorMessage serverMessage = ((PSQLException) nextException).getServerErrorMessage(); + + int position = getErroneousEntryPosition(batchUpdateException); + if (isUniqueConstraintError(serverMessage, config.getEmailPasswordUserToTenantTable(), + "third_party_user_id")) { + + errorByPosition.put(usersToImport.get(position).userId, new DuplicateThirdPartyUserException()); + + } else if (isPrimaryKeyError(serverMessage, config.getThirdPartyUsersTable()) + || isPrimaryKeyError(serverMessage, config.getUsersTable()) + || isPrimaryKeyError(serverMessage, config.getThirdPartyUserToTenantTable()) + || isPrimaryKeyError(serverMessage, config.getAppIdToUserIdTable())) { + errorByPosition.put(usersToImport.get(position).userId, + new io.supertokens.pluginInterface.thirdparty.exception.DuplicateUserIdException()); + } + else if (isForeignKeyConstraintError(serverMessage, config.getAppIdToUserIdTable(), "app_id")) { + throw new TenantOrAppNotFoundException(usersToImport.get(position).tenantIdentifier.toAppIdentifier()); + + } else if (isForeignKeyConstraintError(serverMessage, config.getUsersTable(), "tenant_id")) { + throw new TenantOrAppNotFoundException(usersToImport.get(position).tenantIdentifier); + } + } + nextException = nextException.getNextException(); + } + throw new StorageTransactionLogicException(new BulkImportBatchInsertException("thirdparty errors", errorByPosition)); + } + throw new StorageQueryException(e); } } @@ -1472,6 +1618,16 @@ public boolean doesUserIdExist(TenantIdentifier tenantIdentifier, String userId) } } + @Override + public List findExistingUserIds(AppIdentifier appIdentifier, List userIds) + throws StorageQueryException { + try { + return GeneralQueries.findUserIdsThatExist(this, appIdentifier, userIds); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public AuthRecipeUserInfo getPrimaryUserById(AppIdentifier appIdentifier, String userId) throws StorageQueryException { @@ -1901,13 +2057,56 @@ public void deletePasswordlessUser_Transaction(TransactionConnection con, AppIde @Override public void importPasswordlessUsers_Transaction(TransactionConnection con, - Collection users) - throws StorageQueryException { + List users) + throws StorageQueryException, TenantOrAppNotFoundException { try { Connection sqlCon = (Connection) con.getConnection(); PasswordlessQueries.importUsers_Transaction(sqlCon, this, users); } catch (SQLException e) { - throw new StorageQueryException(e); + if (e instanceof BatchUpdateException) { + Throwable actual = e.getCause(); + if (actual instanceof BatchUpdateException) { + BatchUpdateException batchUpdateException = (BatchUpdateException) actual; + Map errorByPosition = new HashMap<>(); + SQLException nextException = batchUpdateException.getNextException(); + while (nextException != null) { + + if (nextException instanceof PSQLException) { + PostgreSQLConfig config = Config.getConfig(this); + ServerErrorMessage serverMessage = ((PSQLException) nextException).getServerErrorMessage(); + + int position = getErroneousEntryPosition(batchUpdateException); + + if (isPrimaryKeyError(serverMessage, config.getPasswordlessUsersTable()) + || isPrimaryKeyError(serverMessage, config.getUsersTable()) + || isPrimaryKeyError(serverMessage, config.getPasswordlessUserToTenantTable()) + || isPrimaryKeyError(serverMessage, config.getAppIdToUserIdTable())) { + errorByPosition.put(users.get(position).userId, new DuplicateUserIdException()); + } + if (isUniqueConstraintError(serverMessage, config.getPasswordlessUserToTenantTable(), + "email")) { + errorByPosition.put(users.get(position).userId, new DuplicateEmailException()); + + } else if (isUniqueConstraintError(serverMessage, config.getPasswordlessUserToTenantTable(), + "phone_number")) { + errorByPosition.put(users.get(position).userId, new DuplicatePhoneNumberException()); + + } else if (isForeignKeyConstraintError(serverMessage, config.getAppIdToUserIdTable(), + "app_id")) { + throw new TenantOrAppNotFoundException(users.get(position).tenantIdentifier.toAppIdentifier()); + + } else if (isForeignKeyConstraintError(serverMessage, config.getUsersTable(), + "tenant_id")) { + throw new TenantOrAppNotFoundException(users.get(position).tenantIdentifier.toAppIdentifier()); + } + } + nextException = nextException.getNextException(); + } + throw new StorageQueryException( + new BulkImportBatchInsertException("passwordless errors", errorByPosition)); + } + throw new StorageQueryException(e); + } } } @@ -2314,6 +2513,17 @@ public boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, Transactio } } + @Override + public List doesMultipleRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + List roles) throws StorageQueryException { + Connection sqlCon = (Connection) con.getConnection(); + try { + return UserRolesQueries.doesMultipleRoleExist_transaction(this, sqlCon, appIdentifier, roles); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public void createUserIdMapping(AppIdentifier appIdentifier, String superTokensUserId, String externalUserId, @org.jetbrains.annotations.Nullable String externalUserIdInfo) @@ -2349,6 +2559,19 @@ public void createUserIdMapping(AppIdentifier appIdentifier, String superTokensU } + @Override + public void createBulkUserIdMapping(AppIdentifier appIdentifier, + Map superTokensUserIdToExternalUserId) + throws StorageQueryException { + try { + + UserIdMappingQueries.createBulkUserIdMapping(this, appIdentifier, superTokensUserIdToExternalUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + + } + @Override public boolean deleteUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) throws StorageQueryException { @@ -3096,6 +3319,19 @@ public AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(AppIdentifier ap } } + @Override + public AuthRecipeUserInfo[] listPrimaryUsersByMultipleEmailsOrPhoneNumbersOrThirdparty_Transaction( + AppIdentifier appIdentifier, TransactionConnection con, List emails, List phones, + Map thirdpartyIdToThirdpartyUserId) throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return GeneralQueries.listPrimaryUsersByMultipleEmailsOrPhonesOrThirdParty_Transaction(this, sqlCon, + appIdentifier, emails, phones, thirdpartyIdToThirdpartyUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(AppIdentifier appIdentifier, TransactionConnection con, @@ -3279,6 +3515,27 @@ public UserIdMapping[] getUserIdMapping_Transaction(TransactionConnection con, A } } + @Override + public List getMultipleUserIdMapping_Transaction(TransactionConnection connection, + AppIdentifier appIdentifier, List userIds, + boolean isSupertokensIds) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) connection.getConnection(); + List result; + if(isSupertokensIds){ + result = UserIdMappingQueries.getMultipleUserIdMappingWithSupertokensUserId_Transaction(this, + sqlCon, appIdentifier, userIds); + } else { + result = UserIdMappingQueries.getMultipleUserIdMappingWithExternalUserId_Transaction(this, + sqlCon, appIdentifier, userIds); + } + return result; + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier) throws StorageQueryException { @@ -3410,6 +3667,20 @@ public void updateBulkImportUserStatus_Transaction(AppIdentifier appIdentifier, } } + @Override + public void updateMultipleBulkImportUsersStatusToError_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, + @NotNull Map bulkImportUserIdToErrorMessage) + throws StorageQueryException { + Connection sqlCon = (Connection) con.getConnection(); + try { + BulkImportQueries.updateMultipleBulkImportUsersStatusToError_Transaction(this, sqlCon, appIdentifier, + bulkImportUserIdToErrorMessage); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public boolean revokeOAuthTokenByGID(AppIdentifier appIdentifier, String gid) throws StorageQueryException { try { 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 ccd589ac..50edb15c 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java @@ -5,11 +5,13 @@ import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; +import org.jetbrains.annotations.TestOnly; import java.sql.Connection; import java.sql.SQLException; - -import org.jetbrains.annotations.TestOnly; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -133,6 +135,30 @@ public static Long getLastActiveByUserId(Start start, AppIdentifier appIdentifie } } + public static Map getLastActiveByMultipleUserIds(Start start, AppIdentifier appIdentifier, List userIds) + throws StorageQueryException { + String QUERY = "SELECT user_id, last_active_time FROM " + Config.getConfig(start).getUserLastActiveTable() + + " WHERE app_id = ? AND user_id IN ( " + Utils.generateCommaSeperatedQuestionMarks(userIds.size())+ " )"; + + try { + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < userIds.size(); i++) { + pst.setString(2+i, userIds.get(i)); + } + }, res -> { + Map lastActiveByUserIds = new HashMap<>(); + if (res.next()) { + String userId = res.getString("user_id"); + lastActiveByUserIds.put(userId, res.getLong("last_active_time")); + } + return lastActiveByUserIds; + }); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + public static void deleteUserActive_Transaction(Connection con, Start start, AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java index 27a993d8..dd7a243a 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java @@ -29,10 +29,12 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -121,6 +123,32 @@ public static void updateBulkImportUserStatus_Transaction(Start start, Connectio }); } + public static void updateMultipleBulkImportUsersStatusToError_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + @Nonnull Map bulkImportUserIdToErrorMessage) + throws SQLException { + BULK_IMPORT_USER_STATUS errorStatus = BULK_IMPORT_USER_STATUS.FAILED; + String query = "UPDATE " + Config.getConfig(start).getBulkImportUsersTable() + + " SET status = ?, error_msg = ?, updated_at = ? WHERE app_id = ? and id = ?"; + + PreparedStatement setErrorStatement = con.prepareStatement(query); + + int counter = 0; + for(String bulkImportUserId : bulkImportUserIdToErrorMessage.keySet()){ + setErrorStatement.setString(1, errorStatus.toString()); + setErrorStatement.setString(2, bulkImportUserIdToErrorMessage.get(bulkImportUserId)); + setErrorStatement.setLong(3, System.currentTimeMillis()); + setErrorStatement.setString(4, appIdentifier.getAppId()); + setErrorStatement.setString(5, bulkImportUserId); + setErrorStatement.addBatch(); + + if(counter % 100 == 0) { + setErrorStatement.executeBatch(); + } + } + + setErrorStatement.executeBatch(); + } + public static List getBulkImportUsersAndChangeStatusToProcessing(Start start, AppIdentifier appIdentifier, @Nonnull Integer limit) @@ -129,7 +157,7 @@ public static List getBulkImportUsersAndChangeStatusToProcessing return start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); try { - // NOTE: On average, we take about 66 seconds to process 1000 users. If, for any reason, the bulk import users were marked as processing but couldn't be processed within 10 minutes, we'll attempt to process them again. + // NOTE: On average, we take about 60 seconds to process 10k users. If, for any reason, the bulk import users were marked as processing but couldn't be processed within 10 minutes, we'll attempt to process them again. // "FOR UPDATE" ensures that multiple cron jobs don't read the same rows simultaneously. // If one process locks the first 1000 rows, others will wait for the lock to be released. 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 90626511..deb2bfe8 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java @@ -336,94 +336,81 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden }); } - public static void signUpMultiple(Start start, List usersToSignUp) - throws StorageQueryException, StorageTransactionLogicException { - start.startTransaction(con -> { - Connection sqlCon = (Connection) con.getConnection(); - try { - String app_id_to_user_id_QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() - + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; + public static void signUpMultipleForBulkImport_Transaction(Start start, Connection sqlCon, List usersToSignUp) + throws StorageQueryException, StorageTransactionLogicException, SQLException { + try { + String app_id_to_user_id_QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() + + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; + + String all_auth_recipe_users_QUERY = "INSERT INTO " + getConfig(start).getUsersTable() + + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + + "primary_or_recipe_user_time_joined)" + + " VALUES(?, ?, ?, ?, ?, ?, ?)"; + + String emailpassword_users_QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUsersTable() + + "(app_id, user_id, email, password_hash, time_joined)" + " VALUES(?, ?, ?, ?, ?)"; + + String emailpassword_users_to_tenant_QUERY = + "INSERT INTO " + getConfig(start).getEmailPasswordUserToTenantTable() + + "(app_id, tenant_id, user_id, email)" + " VALUES(?, ?, ?, ?)"; - String all_auth_recipe_users_QUERY = "INSERT INTO " + getConfig(start).getUsersTable() + - "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " + - "primary_or_recipe_user_time_joined)" + - " VALUES(?, ?, ?, ?, ?, ?, ?)"; - - String emailpassword_users_QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUsersTable() - + "(app_id, user_id, email, password_hash, time_joined)" + " VALUES(?, ?, ?, ?, ?)"; - - String emailpassword_users_to_tenant_QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUserToTenantTable() - + "(app_id, tenant_id, user_id, email)" + " VALUES(?, ?, ?, ?)"; - - PreparedStatement appIdToUserId = sqlCon.prepareStatement(app_id_to_user_id_QUERY); - PreparedStatement allAuthRecipeUsers = sqlCon.prepareStatement(all_auth_recipe_users_QUERY); - PreparedStatement emailPasswordUsers = sqlCon.prepareStatement(emailpassword_users_QUERY); - PreparedStatement emailPasswordUsersToTenant = sqlCon.prepareStatement(emailpassword_users_to_tenant_QUERY); - - int counter = 0; - for(EmailPasswordImportUser user: usersToSignUp) { - String userId = user.userId; - TenantIdentifier tenantIdentifier = user.tenantIdentifier; - - appIdToUserId.setString(1, tenantIdentifier.getAppId()); - appIdToUserId.setString(2, userId); - appIdToUserId.setString(3, userId); - appIdToUserId.setString(4, EMAIL_PASSWORD.toString()); - appIdToUserId.addBatch(); - - - allAuthRecipeUsers.setString(1, tenantIdentifier.getAppId()); - allAuthRecipeUsers.setString(2, tenantIdentifier.getTenantId()); - allAuthRecipeUsers.setString(3, userId); - allAuthRecipeUsers.setString(4, userId); - allAuthRecipeUsers.setString(5, EMAIL_PASSWORD.toString()); - allAuthRecipeUsers.setLong(6, user.timeJoinedMSSinceEpoch); - allAuthRecipeUsers.setLong(7, user.timeJoinedMSSinceEpoch); - allAuthRecipeUsers.addBatch(); - - emailPasswordUsers.setString(1, tenantIdentifier.getAppId()); - emailPasswordUsers.setString(2, userId); - emailPasswordUsers.setString(3, user.email); - emailPasswordUsers.setString(4, user.passwordHash); - emailPasswordUsers.setLong(5, user.timeJoinedMSSinceEpoch); - emailPasswordUsers.addBatch(); - - emailPasswordUsersToTenant.setString(1, tenantIdentifier.getAppId()); - emailPasswordUsersToTenant.setString(2, tenantIdentifier.getTenantId()); - emailPasswordUsersToTenant.setString(3, userId); - emailPasswordUsersToTenant.setString(4, user.email); - emailPasswordUsersToTenant.addBatch(); - counter++; - if(counter % 100 == 0) { - appIdToUserId.executeBatch(); - allAuthRecipeUsers.executeBatch(); - emailPasswordUsers.executeBatch(); - emailPasswordUsersToTenant.executeBatch(); - } + PreparedStatement appIdToUserId = sqlCon.prepareStatement(app_id_to_user_id_QUERY); + PreparedStatement allAuthRecipeUsers = sqlCon.prepareStatement(all_auth_recipe_users_QUERY); + PreparedStatement emailPasswordUsers = sqlCon.prepareStatement(emailpassword_users_QUERY); + PreparedStatement emailPasswordUsersToTenant = sqlCon.prepareStatement(emailpassword_users_to_tenant_QUERY); + + int counter = 0; + for (EmailPasswordImportUser user : usersToSignUp) { + String userId = user.userId; + TenantIdentifier tenantIdentifier = user.tenantIdentifier; + + appIdToUserId.setString(1, tenantIdentifier.getAppId()); + appIdToUserId.setString(2, userId); + appIdToUserId.setString(3, userId); + appIdToUserId.setString(4, EMAIL_PASSWORD.toString()); + appIdToUserId.addBatch(); + + + allAuthRecipeUsers.setString(1, tenantIdentifier.getAppId()); + allAuthRecipeUsers.setString(2, tenantIdentifier.getTenantId()); + allAuthRecipeUsers.setString(3, userId); + allAuthRecipeUsers.setString(4, userId); + allAuthRecipeUsers.setString(5, EMAIL_PASSWORD.toString()); + allAuthRecipeUsers.setLong(6, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsers.setLong(7, user.timeJoinedMSSinceEpoch); + allAuthRecipeUsers.addBatch(); + + emailPasswordUsers.setString(1, tenantIdentifier.getAppId()); + emailPasswordUsers.setString(2, userId); + emailPasswordUsers.setString(3, user.email); + emailPasswordUsers.setString(4, user.passwordHash); + emailPasswordUsers.setLong(5, user.timeJoinedMSSinceEpoch); + emailPasswordUsers.addBatch(); + + emailPasswordUsersToTenant.setString(1, tenantIdentifier.getAppId()); + emailPasswordUsersToTenant.setString(2, tenantIdentifier.getTenantId()); + emailPasswordUsersToTenant.setString(3, userId); + emailPasswordUsersToTenant.setString(4, user.email); + emailPasswordUsersToTenant.addBatch(); + counter++; + if (counter % 100 == 0) { + appIdToUserId.executeBatch(); + allAuthRecipeUsers.executeBatch(); + emailPasswordUsers.executeBatch(); + emailPasswordUsersToTenant.executeBatch(); } + } - //execute the remaining ones - appIdToUserId.executeBatch(); - allAuthRecipeUsers.executeBatch(); - emailPasswordUsers.executeBatch(); - emailPasswordUsersToTenant.executeBatch(); + //execute the remaining ones + appIdToUserId.executeBatch(); + allAuthRecipeUsers.executeBatch(); + emailPasswordUsers.executeBatch(); + emailPasswordUsersToTenant.executeBatch(); - //UserInfoPartial userInfo = new UserInfoPartial(userId, email, passwordHash, timeJoined); -// fillUserInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); -// fillUserInfoWithVerified_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); - sqlCon.commit(); - //return AuthRecipeUserInfo.create(userId, false, userInfo.toLoginMethod()); - } catch (SQLException throwables) { - throwables.printStackTrace(System.out); - SQLException next = throwables.getNextException(); - while(next != null) { - next.printStackTrace(System.out); - next = next.getNextException(); - } - throw new StorageTransactionLogicException(throwables); - } - return null; - }); + sqlCon.commit(); + } catch (SQLException throwables) { + throw new StorageTransactionLogicException(throwables); + } } public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, @@ -569,6 +556,30 @@ public static String lockEmail_Transaction(Start start, Connection con, }); } + public static List lockEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + List emails) + throws StorageQueryException, SQLException { + if(emails == null || emails.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT user_id FROM " + getConfig(start).getEmailPasswordUsersTable() + + " WHERE app_id = ? AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ") FOR UPDATE"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < emails.size(); i++) { + pst.setString(2 + i, emails.get(i)); + } + }, result -> { + List results = new ArrayList<>(); + while (result.next()) { + results.add(result.getString("user_id")); + } + return results; + }); + } + public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, String email) throws StorageQueryException, SQLException { @@ -612,6 +623,33 @@ public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, }); } + public static List getPrimaryUserIdsUsingMultipleEmails_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + List emails) + throws StorageQueryException, SQLException { + if(emails.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getEmailPasswordUsersTable() + " AS ep" + + " JOIN " + getConfig(start).getAppIdToUserIdTable() + " AS all_users" + + " ON ep.app_id = all_users.app_id AND ep.user_id = all_users.user_id" + + " WHERE ep.app_id = ? AND ep.email IN ( " + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + " )"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < emails.size(); i++) { + pst.setString(2+i, emails.get(i)); + } + }, result -> { + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); + } + return userIds; + }); + } + public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException, UnknownUserIdException { 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 1771608a..12d71e4f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java @@ -524,6 +524,45 @@ public static boolean isUserIdBeingUsedForEmailVerification(Start start, AppIden } } + public static Set findUserIdsBeingUsedForEmailVerification(Start start, AppIdentifier appIdentifier, List userIds) + throws SQLException, StorageQueryException { + + Set foundUserIds = new HashSet<>(); + + String email_verificiation_tokens_QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTokensTable() + + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")"; + + foundUserIds.addAll(execute(start, email_verificiation_tokens_QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < userIds.size(); i++) { + pst.setString(2 + i, userIds.get(i)); + } + }, result -> { + Set userIdsFound = new HashSet<>(); + while (result.next()) { + userIdsFound.add(result.getString("user_id")); + } + return userIdsFound; + })); + + String email_verification_table_QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable() + + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")"; + + foundUserIds.addAll(execute(start, email_verification_table_QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < userIds.size(); i++) { + pst.setString(2 + i, userIds.get(i)); + } + }, result -> { + Set userIdsFound = new HashSet<>(); + while (result.next()) { + userIdsFound.add(result.getString("user_id")); + } + return userIdsFound; + })); + return foundUserIds; + } + public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentifier appIdentifier, String supertokensUserId, String externalUserId) throws StorageQueryException { @@ -560,6 +599,52 @@ public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentif } } + public static void updateMultipleIsEmailVerifiedToExternalUserIds(Start start, AppIdentifier appIdentifier, + Map supertokensUserIdToExternalUserId) + throws StorageQueryException { + try { + start.startTransaction((TransactionConnection con) -> { + Connection sqlCon = (Connection) con.getConnection(); + try { + String update_email_verification_table_query = "UPDATE " + getConfig(start).getEmailVerificationTable() + + " SET user_id = ? WHERE app_id = ? AND user_id = ?"; + String update_email_verification_tokens_table_query = "UPDATE " + getConfig(start).getEmailVerificationTokensTable() + + " SET user_id = ? WHERE app_id = ? AND user_id = ?"; + PreparedStatement updateEmailVerificationQuery = sqlCon.prepareStatement(update_email_verification_table_query); + PreparedStatement updateEmailVerificationTokensQuery = sqlCon.prepareStatement(update_email_verification_tokens_table_query); + + int counter = 0; + for (String supertokensUserId : supertokensUserIdToExternalUserId.keySet()){ + updateEmailVerificationQuery.setString(1, supertokensUserIdToExternalUserId.get(supertokensUserId)); + updateEmailVerificationQuery.setString(2, appIdentifier.getAppId()); + updateEmailVerificationQuery.setString(3, supertokensUserId); + updateEmailVerificationQuery.addBatch(); + + updateEmailVerificationTokensQuery.setString(1, supertokensUserIdToExternalUserId.get(supertokensUserId)); + updateEmailVerificationTokensQuery.setString(2, appIdentifier.getAppId()); + updateEmailVerificationTokensQuery.setString(3, supertokensUserId); + updateEmailVerificationTokensQuery.addBatch(); + + counter++; + if(counter % 100 == 0) { + updateEmailVerificationQuery.executeBatch(); + updateEmailVerificationTokensQuery.executeBatch(); + } + } + updateEmailVerificationQuery.executeBatch(); + updateEmailVerificationTokensQuery.executeBatch(); + + } catch (SQLException e) { + throw new StorageTransactionLogicException(e); + } + + return null; + }); + } catch (StorageTransactionLogicException e) { + throw new StorageQueryException(e.actualException); + } + } + private static class EmailVerificationTokenInfoRowMapper implements RowMapper { private static final EmailVerificationTokenInfoRowMapper INSTANCE = new EmailVerificationTokenInfoRowMapper(); 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 b826fbe5..b0c5c7ee 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -896,6 +896,24 @@ public static boolean doesUserIdExist(Start start, TenantIdentifier tenantIdenti }, ResultSet::next); } + public static List findUserIdsThatExist(Start start, AppIdentifier appIdentifier, List userIds) + throws SQLException, StorageQueryException { + String QUERY = "SELECT user_id FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? AND user_id IN ("+ Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for(int i = 0; i { + List foundUserIds = new ArrayList<>(); + while(result.next()){ + foundUserIds.add(result.getString(1)); + } + return foundUserIds; + }); + } + public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenantIdentifier, @NotNull Integer limit, @NotNull String timeJoinedOrder, @Nullable RECIPE_ID[] includeRecipeIds, @Nullable String userId, @@ -1480,6 +1498,39 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(Start sta return result.toArray(new AuthRecipeUserInfo[0]); } + public static AuthRecipeUserInfo[] listPrimaryUsersByMultipleEmailsOrPhonesOrThirdParty_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List emails, List phones, + Map thirdpartyUserIdToThirdpartyId) + throws SQLException, StorageQueryException { + Set userIds = new HashSet<>(); + + //I am not really sure this is really needed.. + EmailPasswordQueries.lockEmail_Transaction(start, sqlCon, appIdentifier, emails); + ThirdPartyQueries.lockEmail_Transaction(start, sqlCon, appIdentifier, emails); + PasswordlessQueries.lockEmail_Transaction(start, sqlCon, appIdentifier, emails); + PasswordlessQueries.lockPhoneAndTenant_Transaction(start, sqlCon, appIdentifier, phones); + ThirdPartyQueries.lockThirdPartyInfoAndTenant_Transaction(start, sqlCon, appIdentifier, thirdpartyUserIdToThirdpartyId); + + //collect ids by email + userIds.addAll(EmailPasswordQueries.getPrimaryUserIdsUsingMultipleEmails_Transaction(start, sqlCon, appIdentifier, + emails)); + userIds.addAll(PasswordlessQueries.getPrimaryUserIdsUsingMultipleEmails_Transaction(start, sqlCon, appIdentifier, + emails)); + userIds.addAll(ThirdPartyQueries.getPrimaryUserIdsUsingMultipleEmails_Transaction(start, sqlCon, appIdentifier, emails)); + + //collect ids by phone + userIds.addAll(PasswordlessQueries.listUserIdsByMultiplePhoneNumber_Transaction(start, sqlCon, appIdentifier, phones)); + + //collect ids by thirdparty + userIds.addAll(ThirdPartyQueries.listUserIdsByMultipleThirdPartyInfo_Transaction(start, sqlCon, appIdentifier, thirdpartyUserIdToThirdpartyId)); + + List result = getPrimaryUserInfoForUserIds_Transaction(start, sqlCon, appIdentifier, + new ArrayList<>(userIds)); + + return result.toArray(new AuthRecipeUserInfo[0]); + } + public static AuthRecipeUserInfo[] listPrimaryUsersByEmail(Start start, TenantIdentifier tenantIdentifier, String email) throws StorageQueryException, SQLException { @@ -1699,37 +1750,20 @@ private static List getPrimaryUserInfoForUserIds_Transaction // which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column, // or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id // column -// String QUERY = -// "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + -// "aaru.tenant_id, aaru.time_joined " + -// "FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" + -// " LEFT JOIN " + getConfig(start).getUsersTable() + -// " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + -// " WHERE au.primary_or_recipe_user_id IN " + -// " (SELECT primary_or_recipe_user_id FROM " + -// getConfig(start).getAppIdToUserIdTable() + -// " WHERE (user_id IN (" -// + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +") " + -// " OR primary_or_recipe_user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")) " + -// " AND app_id = ?) " + -// "AND au.app_id = ?"; - - String QUERY = "SELECT" + - " au.user_id," + - " au.primary_or_recipe_user_id," + - " au.is_linked_or_is_a_primary_user," + - " au.recipe_id," + - " aaru.tenant_id," + - " aaru.time_joined" + - " FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" + - " LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id" + - " AND au.user_id = aaru.user_id" + - " LEFT JOIN " + getConfig(start).getAppIdToUserIdTable() + " as aiui ON au.primary_or_recipe_user_id = aiui.user_id" + - " AND aiui.app_id = au.app_id" + - " WHERE" + - " aiui.user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + ")" + - " OR au.primary_or_recipe_user_id IN ("+ Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")" + - " AND au.app_id = ?"; + String QUERY = + "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " + + "aaru.tenant_id, aaru.time_joined " + + "FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" + + " LEFT JOIN " + getConfig(start).getUsersTable() + + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" + + " WHERE au.primary_or_recipe_user_id IN " + + " (SELECT primary_or_recipe_user_id FROM " + + getConfig(start).getAppIdToUserIdTable() + + " WHERE (user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +") " + + " OR primary_or_recipe_user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")) " + + " AND app_id = ?) " + + "AND au.app_id = ?"; List allAuthUsersResult = execute(sqlCon, QUERY, pst -> { // IN user_id @@ -1743,6 +1777,7 @@ private static List getPrimaryUserInfoForUserIds_Transaction } // for app_id pst.setString(index, appIdentifier.getAppId()); + pst.setString(index+1, appIdentifier.getAppId()); // System.out.println(pst); }, result -> { List parsedResult = new ArrayList<>(); 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 eed57c31..6d3089cd 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java @@ -814,6 +814,30 @@ public static List lockEmail_Transaction(Start start, Connection con, Ap }); } + public static List lockEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + List emails) + throws StorageQueryException, SQLException { + if(emails == null || emails.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT user_id FROM " + getConfig(start).getPasswordlessUsersTable() + + " WHERE app_id = ? AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ") FOR UPDATE"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < emails.size(); i++) { + pst.setString(2 + i, emails.get(i)); + } + }, result -> { + List results = new ArrayList<>(); + while (result.next()) { + results.add(result.getString("user_id")); + } + return results; + }); + } + public static List lockPhoneAndTenant_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String phoneNumber) @@ -833,6 +857,30 @@ public static List lockPhoneAndTenant_Transaction(Start start, Connectio }); } + public static List lockPhoneAndTenant_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + List phones) + throws StorageQueryException, SQLException { + if(phones == null || phones.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT user_id FROM " + getConfig(start).getPasswordlessUsersTable() + + " WHERE app_id = ? AND phone_number IN (" + Utils.generateCommaSeperatedQuestionMarks(phones.size()) + ") FOR UPDATE"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < phones.size(); i++) { + pst.setString(2 + i, phones.get(i)); + } + }, result -> { + List results = new ArrayList<>(); + while (result.next()) { + results.add(result.getString("user_id")); + } + return results; + }); + } + public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, String email) throws StorageQueryException, SQLException { @@ -876,6 +924,33 @@ public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, }); } + public static List getPrimaryUserIdsUsingMultipleEmails_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + List emails) + throws StorageQueryException, SQLException { + if(emails.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS ep" + + " JOIN " + getConfig(start).getAppIdToUserIdTable() + " AS all_users" + + " ON ep.app_id = all_users.app_id AND ep.user_id = all_users.user_id" + + " WHERE ep.app_id = ? AND ep.email IN ( " + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + " )"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < emails.size(); i++) { + pst.setString(2+i, emails.get(i)); + } + }, result -> { + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); + } + return userIds; + }); + } + public static String getPrimaryUserByPhoneNumber(Start start, TenantIdentifier tenantIdentifier, @Nonnull String phoneNumber) throws StorageQueryException, SQLException { @@ -919,6 +994,33 @@ public static List listUserIdsByPhoneNumber_Transaction(Start start, Con }); } + public static List listUserIdsByMultiplePhoneNumber_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + @Nonnull List phoneNumbers) + throws StorageQueryException, SQLException { + if(phoneNumbers.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" + + " JOIN " + getConfig(start).getUsersTable() + " AS all_users" + + " ON pless.app_id = all_users.app_id AND pless.user_id = all_users.user_id" + + " WHERE pless.app_id = ? AND pless.phone_number IN ( "+ Utils.generateCommaSeperatedQuestionMarks(phoneNumbers.size()) +" )"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < phoneNumbers.size(); i++) { + pst.setString(2 + i, phoneNumbers.get(i)); + } + }, result -> { + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); + } + return userIds; + }); + } + public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException, SQLException, UnknownUserIdException { 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 0fe56e4d..c5108fd4 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java @@ -33,7 +33,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -306,6 +308,31 @@ public static String[] getAllNonExpiredSessionHandlesForUser(Start start, AppIde }); } + public static Map> getAllNonExpiredSessionHandlesForUsers(Start start, AppIdentifier appIdentifier, + List userIds) + throws SQLException, StorageQueryException { + String QUERY = "SELECT user_id, session_handle FROM " + getConfig(start).getSessionInfoTable() + + " WHERE app_id = ? AND expires_at >= ? AND user_id IN ( " + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + " )"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setLong(2, currentTimeMillis()); + for(int i = 0; i < userIds.size() ; i++){ + pst.setString(3 + i, userIds.get(i)); + } + }, result -> { + Map> temp = new HashMap<>(); + while (result.next()) { + String userId = result.getString("user_id"); + if(!temp.containsKey(userId)){ + temp.put(userId, new ArrayList<>()); + } + temp.get(userId).add(result.getString("session_handle")); + } + return temp; + }); + } + public static void deleteAllExpiredSessions(Start start) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getSessionInfoTable() + " WHERE expires_at <= ?"; 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 a364da86..c14aa15d 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java @@ -15,7 +15,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -291,6 +293,30 @@ public static TOTPDevice[] getDevices(Start start, AppIdentifier appIdentifier, }); } + public static Map> getDevicesForMultipleUsers(Start start, AppIdentifier appIdentifier, List userIds) + throws StorageQueryException, SQLException { + String QUERY = "SELECT * FROM " + Config.getConfig(start).getTotpUserDevicesTable() + + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + ");"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for(int i = 0; i < userIds.size(); i++) { + pst.setString(2+i, userIds.get(i)); + } + }, result -> { + Map> devicesByUserIds = new HashMap<>(); + while (result.next()) { + String userId = result.getString("user_id"); + if (!devicesByUserIds.containsKey(userId)){ + devicesByUserIds.put(userId, new ArrayList<>()); + } + devicesByUserIds.get(userId).add(TOTPDeviceRowMapper.getInstance().map(result)); + } + + return devicesByUserIds; + }); + } + public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { 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 6c8be437..afe63b55 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java @@ -222,6 +222,30 @@ public static List lockEmail_Transaction(Start start, Connection con, }); } + public static List lockEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + List emails) + throws StorageQueryException, SQLException { + if(emails == null || emails.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT user_id FROM " + getConfig(start).getThirdPartyUsersTable() + + " WHERE app_id = ? AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ") FOR UPDATE"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < emails.size(); i++) { + pst.setString(2 + i, emails.get(i)); + } + }, result -> { + List results = new ArrayList<>(); + while (result.next()) { + results.add(result.getString("user_id")); + } + return results; + }); + } + public static List lockThirdPartyInfoAndTenant_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String thirdPartyId, String thirdPartyUserId) @@ -243,6 +267,38 @@ public static List lockThirdPartyInfoAndTenant_Transaction(Start start, }); } + public static List lockThirdPartyInfoAndTenant_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + Map thirdPartyUserIdToThirdPartyId) + throws SQLException, StorageQueryException { + if(thirdPartyUserIdToThirdPartyId == null || thirdPartyUserIdToThirdPartyId.isEmpty()) { + return new ArrayList<>(); + } + + String QUERY = "SELECT user_id " + + " FROM " + getConfig(start).getThirdPartyUsersTable() + + " WHERE app_id = ? AND third_party_id IN ("+Utils.generateCommaSeperatedQuestionMarks( + thirdPartyUserIdToThirdPartyId.size())+") AND third_party_user_id IN ("+ + Utils.generateCommaSeperatedQuestionMarks(thirdPartyUserIdToThirdPartyId.size())+") FOR UPDATE"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + int counter = 2; + for (String thirdPartyId : thirdPartyUserIdToThirdPartyId.values()){ + pst.setString(counter++, thirdPartyId); + } + for (String thirdPartyUserId : thirdPartyUserIdToThirdPartyId.keySet()) { + pst.setString(counter++, thirdPartyUserId); + } + }, result -> { + List finalResult = new ArrayList<>(); + while (result.next()) { + finalResult.add(result.getString("user_id")); + } + return finalResult; + }); + } + public static List getUsersInfoUsingIdList(Start start, Set ids, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { @@ -353,6 +409,41 @@ public static List listUserIdsByThirdPartyInfo_Transaction(Start start, }); } + public static List listUserIdsByMultipleThirdPartyInfo_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + Map thirdPartyUserIdToThirdPartyId) + throws SQLException, StorageQueryException { + if(thirdPartyUserIdToThirdPartyId.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getThirdPartyUsersTable() + " AS tp" + + " JOIN " + getConfig(start).getUsersTable() + " AS all_users" + + " ON tp.app_id = all_users.app_id AND tp.user_id = all_users.user_id" + + " WHERE tp.app_id = ? AND tp.third_party_id IN ( " + Utils.generateCommaSeperatedQuestionMarks( + thirdPartyUserIdToThirdPartyId.size()) + " ) AND tp.third_party_user_id IN ( " + Utils.generateCommaSeperatedQuestionMarks( + thirdPartyUserIdToThirdPartyId.size()) + " )"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + int counter = 2; + for (String thirdpartId : thirdPartyUserIdToThirdPartyId.values()){ + pst.setString(counter, thirdpartId); + counter++; + } + for (String thirdparyUserId : thirdPartyUserIdToThirdPartyId.keySet()){ + pst.setString(counter, thirdparyUserId); + counter++; + } + }, result -> { + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); + } + return userIds; + }); + } + public static String getUserIdByThirdPartyInfo(Start start, TenantIdentifier tenantIdentifier, String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { @@ -455,6 +546,33 @@ public static List getPrimaryUserIdUsingEmail_Transaction(Start start, C }); } + public static List getPrimaryUserIdsUsingMultipleEmails_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + List emails) + throws StorageQueryException, SQLException { + if(emails.isEmpty()){ + return new ArrayList<>(); + } + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getThirdPartyUsersTable() + " AS ep" + + " JOIN " + getConfig(start).getAppIdToUserIdTable() + " AS all_users" + + " ON ep.app_id = all_users.app_id AND ep.user_id = all_users.user_id" + + " WHERE ep.app_id = ? AND ep.email IN ( " + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + " )"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < emails.size(); i++) { + pst.setString(2+i, emails.get(i)); + } + }, result -> { + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); + } + return userIds; + }); + } + public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException, UnknownUserIdException { 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 072571c1..48ea4dd1 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java @@ -20,17 +20,20 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.useridmapping.UserIdMapping; +import io.supertokens.storage.postgresql.ConnectionPool; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; import javax.annotation.Nullable; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -80,6 +83,30 @@ public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, }); } + public static void createBulkUserIdMapping(Start start, AppIdentifier appIdentifier, + Map superTokensUserIdToExternalUserId) + throws SQLException, StorageQueryException { + String QUERY = "INSERT INTO " + Config.getConfig(start).getUserIdMappingTable() + + " (app_id, supertokens_user_id, external_user_id)" + " VALUES(?, ?, ?)"; + + Connection sqlConnection = ConnectionPool.getConnection(start); + PreparedStatement insertStatement = sqlConnection.prepareStatement(QUERY); + + int counter = 0; + for(String superTokensUserId : superTokensUserIdToExternalUserId.keySet()) { + insertStatement.setString(1, appIdentifier.getAppId()); + insertStatement.setString(2, superTokensUserId); + insertStatement.setString(3, superTokensUserIdToExternalUserId.get(superTokensUserId)); + insertStatement.addBatch(); + + counter++; + if(counter % 100 == 0) { + insertStatement.executeBatch(); + } + } + insertStatement.executeBatch(); + } + public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { @@ -304,6 +331,50 @@ public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start }); } + public static List getMultipleUserIdMappingWithExternalUserId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userId) + throws SQLException, StorageQueryException { + String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + + " WHERE app_id = ? AND external_user_id IN ( "+ Utils.generateCommaSeperatedQuestionMarks( + userId.size()) + " )"; + + return execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for(int i = 0; i < userId.size(); i++) { + pst.setString(2 + i, userId.get(i)); + } + }, result -> { + List results = new ArrayList<>(); + while (result.next()) { + results.add(UserIdMappingRowMapper.getInstance().mapOrThrow(result)); + } + return results; + }); + } + + public static List getMultipleUserIdMappingWithSupertokensUserId_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userId) + throws SQLException, StorageQueryException { + String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + + " WHERE app_id = ? AND supertokens_user_id IN ( "+ Utils.generateCommaSeperatedQuestionMarks( + userId.size()) + " )"; + + return execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for(int i = 0; i < userId.size(); i++) { + pst.setString(2 + i, userId.get(i)); + } + }, result -> { + List results = new ArrayList<>(); + while (result.next()) { + results.add(UserIdMappingRowMapper.getInstance().mapOrThrow(result)); + } + return results; + }); + } + public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, 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 cd928196..d4fde3d7 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java @@ -19,6 +19,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; @@ -176,4 +177,11 @@ public static JsonObject getUserMetadata(Start start, AppIdentifier appIdentifie return null; }); } + + public static Map getMultipleUserMetadatas(Start start, AppIdentifier appIdentifier, List userIds) + throws StorageQueryException, StorageTransactionLogicException { + return start.startTransaction(con -> { + return getMultipleUsersMetadatas_Transaction(start, (Connection) con.getConnection(), appIdentifier, userIds); + }); + } } 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 212ba35d..1985082d 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java @@ -28,6 +28,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; @@ -263,6 +265,29 @@ public static String[] getRolesForUser(Start start, AppIdentifier appIdentifier, }); } + public static Map> getRolesForUsers(Start start, AppIdentifier appIdentifier, List userIds) + throws SQLException, StorageQueryException { + String QUERY = "SELECT user_id, role FROM " + getConfig(start).getUserRolesTable() + + " WHERE app_id = ? AND user_id IN ("+Utils.generateCommaSeperatedQuestionMarks(userIds.size())+") ;"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for(int i = 0; i < userIds.size(); i++) { + pst.setString(2+i, userIds.get(i)); + } + }, result -> { + Map> rolesByUserId = new HashMap<>(); + while (result.next()) { + String userId = result.getString("user_id"); + if(!rolesByUserId.containsKey(userId)) { + rolesByUserId.put(userId, new ArrayList<>()); + } + rolesByUserId.get(userId).add(result.getString("role")); + } + return rolesByUserId; + }); + } + public static boolean deleteRoleForUser_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String userId, String role) throws SQLException, StorageQueryException { @@ -290,6 +315,29 @@ public static boolean doesRoleExist_transaction(Start start, Connection con, App }, ResultSet::next); } + public static List doesMultipleRoleExist_transaction(Start start, Connection con, AppIdentifier appIdentifier, + List roles) + throws SQLException, StorageQueryException { + String QUERY = "SELECT role FROM " + getConfig(start).getRolesTable() + + " WHERE app_id = ? AND role IN (" +Utils.generateCommaSeperatedQuestionMarks(roles.size())+ ") FOR UPDATE"; + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + for (int i = 0; i < roles.size(); i++) { + pst.setString(2+i, roles.get(i)); + } + }, result -> { + List rolesExistsAnswer = new ArrayList<>(); + List rolesFound = new ArrayList<>(); + while(result.next()){ + rolesFound.add(result.getString("role")); + } + for(String role : roles){ + rolesExistsAnswer.add(rolesFound.contains(role)); + } + return rolesExistsAnswer; + }); + } + public static String[] getUsersForRole(Start start, TenantIdentifier tenantIdentifier, String role) throws SQLException, StorageQueryException { String QUERY = "SELECT user_id FROM " + getConfig(start).getUserRolesTable() From ec02abb93ab48e82ee81a17a9b7a4cb3b70b0c07 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Tue, 26 Nov 2024 21:49:13 +0100 Subject: [PATCH 44/45] fix: fixing tests --- .../supertokens/storage/postgresql/Start.java | 4 +-- .../postgresql/queries/BulkImportQueries.java | 5 ++- .../queries/EmailVerificationQueries.java | 4 +-- .../postgresql/queries/UserRolesQueries.java | 35 +++++++++---------- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 59718e6a..7d12083e 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -2299,7 +2299,7 @@ public void addRoleToUser(TenantIdentifier tenantIdentifier, String userId, Stri @Override public void addRolesToUsers_Transaction(TransactionConnection connection, - Map> rolesToUserByTenants) + Map>> rolesToUserByTenants) throws StorageQueryException { try { UserRolesQueries.addRolesToUsers_Transaction(this, (Connection) connection.getConnection(), rolesToUserByTenants); @@ -2514,7 +2514,7 @@ public boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, Transactio } @Override - public List doesMultipleRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + public List doesMultipleRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, List roles) throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); try { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java index dd7a243a..f4874540 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/BulkImportQueries.java @@ -157,14 +157,14 @@ public static List getBulkImportUsersAndChangeStatusToProcessing return start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); try { - // NOTE: On average, we take about 60 seconds to process 10k users. If, for any reason, the bulk import users were marked as processing but couldn't be processed within 10 minutes, we'll attempt to process them again. // "FOR UPDATE" ensures that multiple cron jobs don't read the same rows simultaneously. // If one process locks the first 1000 rows, others will wait for the lock to be released. // "SKIP LOCKED" allows other processes to skip locked rows and select the next 1000 available rows. String selectQuery = "SELECT * FROM " + Config.getConfig(start).getBulkImportUsersTable() + " WHERE app_id = ?" - + " AND (status = 'NEW' OR (status = 'PROCESSING' AND updated_at < (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000) - 10 * 60 * 1000))" /* 10 mins */ + //+ " AND (status = 'NEW' OR (status = 'PROCESSING' AND updated_at < (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000) - 10 * 60 * 1000))" /* 10 mins */ + + " AND (status = 'NEW' OR status = 'PROCESSING' )" + " LIMIT ? FOR UPDATE SKIP LOCKED"; List bulkImportUsers = new ArrayList<>(); @@ -249,7 +249,6 @@ public static List getBulkImportUsers(Start start, AppIdentifier public static List deleteBulkImportUsers(Start start, AppIdentifier appIdentifier, @Nonnull String[] bulkImportUserIds) throws SQLException, StorageQueryException { - System.out.println("Deleting bulkimportuser ids: " + bulkImportUserIds.length); if (bulkImportUserIds.length == 0) { return new ArrayList<>(); } 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 12d71e4f..fc3fa372 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java @@ -137,8 +137,8 @@ public static void updateMultipleUsersIsEmailVerified_Transaction(Start start, C int counter = 0; for(Map.Entry emailToUser : emailToUserIds.entrySet()){ insertQuery.setString(1, appIdentifier.getAppId()); - insertQuery.setString(2, emailToUser.getValue()); - insertQuery.setString(3, emailToUser.getKey()); + insertQuery.setString(2, emailToUser.getKey()); + insertQuery.setString(3, emailToUser.getValue()); insertQuery.addBatch(); counter++; 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 1985082d..807c8bf3 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java @@ -204,25 +204,26 @@ public static int addRoleToUser(Start start, TenantIdentifier tenantIdentifier, }); } - public static void addRolesToUsers_Transaction(Start start, Connection connection, Map> rolesToUserByTenants) //tenant -> user -> role + public static void addRolesToUsers_Transaction(Start start, Connection connection, Map>> rolesToUserByTenants) //tenant -> user -> role throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getUserRolesTable() + "(app_id, tenant_id, user_id, role) VALUES(?, ?, ?, ?);"; PreparedStatement insertStatement = connection.prepareStatement(QUERY); int counter = 0; - for(Map.Entry> tenantsEntry : rolesToUserByTenants.entrySet()) { - for(Map.Entry rolesToUser : tenantsEntry.getValue().entrySet()) { - - insertStatement.setString(1, tenantsEntry.getKey().getAppId()); - insertStatement.setString(2, tenantsEntry.getKey().getTenantId()); - insertStatement.setString(3, rolesToUser.getKey()); - insertStatement.setString(4, rolesToUser.getValue()); - insertStatement.addBatch(); - counter++; - - if(counter % 100 == 0) { - insertStatement.executeBatch(); + for(Map.Entry>> tenantsEntry : rolesToUserByTenants.entrySet()) { + for(Map.Entry> rolesToUser : tenantsEntry.getValue().entrySet()) { + for(String roleForUser : rolesToUser.getValue()){ + insertStatement.setString(1, tenantsEntry.getKey().getAppId()); + insertStatement.setString(2, tenantsEntry.getKey().getTenantId()); + insertStatement.setString(3, rolesToUser.getKey()); + insertStatement.setString(4, roleForUser); + insertStatement.addBatch(); + counter++; + + if(counter % 100 == 0) { + insertStatement.executeBatch(); + } } } } @@ -315,7 +316,7 @@ public static boolean doesRoleExist_transaction(Start start, Connection con, App }, ResultSet::next); } - public static List doesMultipleRoleExist_transaction(Start start, Connection con, AppIdentifier appIdentifier, + public static List doesMultipleRoleExist_transaction(Start start, Connection con, AppIdentifier appIdentifier, List roles) throws SQLException, StorageQueryException { String QUERY = "SELECT role FROM " + getConfig(start).getRolesTable() @@ -326,15 +327,11 @@ public static List doesMultipleRoleExist_transaction(Start start, Conne pst.setString(2+i, roles.get(i)); } }, result -> { - List rolesExistsAnswer = new ArrayList<>(); List rolesFound = new ArrayList<>(); while(result.next()){ rolesFound.add(result.getString("role")); } - for(String role : roles){ - rolesExistsAnswer.add(rolesFound.contains(role)); - } - return rolesExistsAnswer; + return rolesFound; }); } From 05edffc2dc4cf99d0e106d4c4635daf6bc57c4a0 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Wed, 27 Nov 2024 13:52:44 +0100 Subject: [PATCH 45/45] chore: changelog and build version update --- CHANGELOG.md | 28 ++++++++++++++++++++++++++-- build.gradle | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 188fd938..1d781000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,33 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -- Unrestricts the connection pool size for the Bulk Import -- Adds queries for Bulk Import +## [7.3.0] +- Adds tables and queries for Bulk Import + +### Migration + +```sql +"CREATE TABLE IF NOT EXISTS bulk_import_users ( + id CHAR(36), + app_id VARCHAR(64) NOT NULL DEFAULT 'public', + primary_user_id VARCHAR(36), + raw_data TEXT NOT NULL, + status VARCHAR(128) DEFAULT 'NEW', + error_msg TEXT, + created_at BIGINT NOT NULL, + updated_at BIGINT NOT NULL, + CONSTRAINT bulk_import_users_pkey PRIMARY KEY(app_id, id), + CONSTRAINT bulk_import_users__app_id_fkey FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS bulk_import_users_status_updated_at_index ON bulk_import_users (app_id, status, updated_at); + +CREATE INDEX IF NOT EXISTS bulk_import_users_pagination_index1 ON bulk_import_users (app_id, status, created_at DESC, + id DESC); + +CREATE INDEX IF NOT EXISTS bulk_import_users_pagination_index2 ON bulk_import_users (app_id, created_at DESC, id DESC); +``` ## [7.2.0] - 2024-10-03 - Compatible with plugin interface version 6.3 diff --git a/build.gradle b/build.gradle index 1b24a626..112bd9a1 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "7.2.1" +version = "7.3.0" repositories { mavenCentral()