From 449dc14eb06fcf9cdd739717c2a87818fde9ff9c Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 20 Sep 2023 11:56:46 +0530 Subject: [PATCH] feat: Account linking (#158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * up to speed iwth in mem db * bug fix * adds link account function * removes unneeded index * Account linking function changes (#136) * fixes bugs * more tests * Link accounts (#137) * fixes bugs * more tests * updates to function (#138) * Account linking unlink accounts (#139) * updates to function * adds unlink account function * fixes and adds test (#140) * changes for password reset flow (#141) * Account linking update email (#142) * changes for password reset flow * removes unneeded function * removes unneeded function * adds recipe user id in session (#143) * fixes query (#144) * fix: user pagination (#145) * fix: query update * fix: primary_or_recipe_user_time_joined added to all_auth_users table * fix: primary_or_recipe_user_time_joined added to all_auth_users table * fix: user pagination queries * fix: user pagination queries * fix: plugin interface fix (#146) * fix: plugin interface fix * fix: pr comments * fix: pr comments * fix: External userid (#147) * fix: plugin interface fix * fix: pr comments * fix: pr comments * fix: external userid * fix: remove UserInfo class (#148) * fix: multitenant user association with account linking (#149) * fix: tenant association query fixes * fix: multitenancy related changes * fix: pr comments * fix: pr comments * fix: pr comments * fix: remove con reuse (#150) * fix: remove con reuse * fix: pr comments * fix: pr comments * fix: pr comments * fix: pr comments * fix: index updates (#152) * fix: updated indexes * fix: pr comments * fix: pr comments * fix: fkey constraint for primary_or_recipe_user_id (#153) * fix: account linking stats (#154) * fix: account linking stats * fix: query * fix: pr comment * fix: fixing tenant association * fix: allow user disassociation from all tenant (#156) * fix: allow user disassociation from all tenant * fix: query * fix: useridmapping functions (#157) * fix: version and changelog * fix: version and changelog * fix: time joined fix --------- Co-authored-by: rishabhpoddar Co-authored-by: Mihály Lengyel --- CHANGELOG.md | 91 ++ build.gradle | 2 +- jar/postgresql-plugin-4.0.2.jar | Bin 188122 -> 0 bytes jar/postgresql-plugin-5.0.0.jar | Bin 0 -> 206459 bytes pluginInterfaceSupported.json | 2 +- .../supertokens/storage/postgresql/Start.java | 580 +++++++----- .../queries/ActiveUsersQueries.java | 65 +- .../queries/EmailPasswordQueries.java | 450 +++++++--- .../queries/EmailVerificationQueries.java | 180 +++- .../postgresql/queries/GeneralQueries.java | 830 ++++++++++++++++-- .../queries/PasswordlessQueries.java | 527 ++++++++--- .../postgresql/queries/SessionQueries.java | 88 +- .../postgresql/queries/ThirdPartyQueries.java | 433 ++++++--- .../queries/UserIdMappingQueries.java | 51 ++ .../queries/UserMetadataQueries.java | 24 +- .../postgresql/queries/UserRolesQueries.java | 44 +- .../storage/postgresql/utils/Utils.java | 11 + .../postgresql/test/AccountLinkingTests.java | 154 ++++ .../postgresql/test/ExceptionParsingTest.java | 42 +- .../TestUserPoolIdChangeBehaviour.java | 13 +- 20 files changed, 2753 insertions(+), 834 deletions(-) delete mode 100644 jar/postgresql-plugin-4.0.2.jar create mode 100644 jar/postgresql-plugin-5.0.0.jar create mode 100644 src/test/java/io/supertokens/storage/postgresql/test/AccountLinkingTests.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 95dd119e..496fcff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,97 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [5.0.0] - 2023-09-19 + +### 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 + +### Migration + +1. Ensure that the core is already upgraded to the version 6.0.13 (CDI version 3.0) +2. Stop the core instance(s) +3. Run the migration script + ```sql + ALTER TABLE all_auth_recipe_users + ADD COLUMN primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT ('0'); + + ALTER TABLE all_auth_recipe_users + ADD COLUMN is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE; + + ALTER TABLE all_auth_recipe_users + ADD COLUMN primary_or_recipe_user_time_joined BIGINT NOT NULL DEFAULT 0; + + UPDATE all_auth_recipe_users + SET primary_or_recipe_user_id = user_id + WHERE primary_or_recipe_user_id = '0'; + + UPDATE all_auth_recipe_users + SET primary_or_recipe_user_time_joined = time_joined + WHERE primary_or_recipe_user_time_joined = 0; + + ALTER TABLE all_auth_recipe_users + ADD CONSTRAINT all_auth_recipe_users_primary_or_recipe_user_id_fkey + FOREIGN KEY (app_id, primary_or_recipe_user_id) + REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; + + ALTER TABLE all_auth_recipe_users + ALTER primary_or_recipe_user_id DROP DEFAULT; + + ALTER TABLE app_id_to_user_id + ADD COLUMN primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT ('0'); + + ALTER TABLE app_id_to_user_id + ADD COLUMN is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE; + + UPDATE app_id_to_user_id + SET primary_or_recipe_user_id = user_id + WHERE primary_or_recipe_user_id = '0'; + + ALTER TABLE app_id_to_user_id + ADD CONSTRAINT app_id_to_user_id_primary_or_recipe_user_id_fkey + FOREIGN KEY (app_id, primary_or_recipe_user_id) + REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; + + ALTER TABLE app_id_to_user_id + ALTER primary_or_recipe_user_id DROP DEFAULT; + + DROP INDEX all_auth_recipe_users_pagination_index; + + CREATE INDEX all_auth_recipe_users_pagination_index1 ON all_auth_recipe_users ( + app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC); + + CREATE INDEX all_auth_recipe_users_pagination_index2 ON all_auth_recipe_users ( + app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC); + + CREATE INDEX all_auth_recipe_users_pagination_index3 ON all_auth_recipe_users ( + recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC); + + CREATE INDEX all_auth_recipe_users_pagination_index4 ON all_auth_recipe_users ( + recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC); + + CREATE INDEX all_auth_recipe_users_primary_user_id_index ON all_auth_recipe_users (primary_or_recipe_user_id, app_id); + + CREATE INDEX all_auth_recipe_users_recipe_id_index ON all_auth_recipe_users (app_id, recipe_id, tenant_id); + + ALTER TABLE emailpassword_pswd_reset_tokens DROP CONSTRAINT IF EXISTS emailpassword_pswd_reset_tokens_user_id_fkey; + + ALTER TABLE emailpassword_pswd_reset_tokens ADD CONSTRAINT emailpassword_pswd_reset_tokens_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id, user_id) ON DELETE CASCADE; + + ALTER TABLE emailpassword_pswd_reset_tokens ADD COLUMN email VARCHAR(256); + ``` +4. Run the new instance(s) of the core (version 7.0.0) + + ## [4.0.2] - Fixes null pointer issue when user belongs to no tenant. diff --git a/build.gradle b/build.gradle index c26e5e96..a3e8c53f 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "4.0.2" +version = "5.0.0" repositories { mavenCentral() diff --git a/jar/postgresql-plugin-4.0.2.jar b/jar/postgresql-plugin-4.0.2.jar deleted file mode 100644 index b4c244925334c54f5af1823beb1d867af996737a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188122 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

W7=6%+~XG^v=LBwUUygpp=k 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

3lqIGUa#}Yu#c84LIK|V%_ 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%0AaXGwKDJ3wB> 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 zü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 dk;PuW3U!j6yN^I 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≠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 literal 0 HcmV?d00001 diff --git a/pluginInterfaceSupported.json b/pluginInterfaceSupported.json index 431c3b08..a5fdc62c 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": [ - "3.0" + "4.0" ] } \ 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 30cd77ba..505bb9fb 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -23,13 +23,14 @@ import com.zaxxer.hikari.pool.HikariPool; import io.supertokens.pluginInterface.*; import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; +import io.supertokens.pluginInterface.authRecipe.LoginMethod; +import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage; import io.supertokens.pluginInterface.dashboard.DashboardSearchTags; import io.supertokens.pluginInterface.dashboard.DashboardSessionInfo; 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.PasswordResetTokenInfo; -import io.supertokens.pluginInterface.emailpassword.UserInfo; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicatePasswordResetTokenException; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException; @@ -55,6 +56,7 @@ 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.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; import io.supertokens.pluginInterface.passwordless.exception.*; @@ -77,6 +79,7 @@ import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.useridmapping.exception.UnknownSuperTokensUserIdException; import io.supertokens.pluginInterface.useridmapping.exception.UserIdMappingAlreadyExistsException; +import io.supertokens.pluginInterface.useridmapping.sqlStorage.UserIdMappingSQLStorage; import io.supertokens.pluginInterface.usermetadata.UserMetadataStorage; import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; import io.supertokens.pluginInterface.userroles.UserRolesStorage; @@ -98,12 +101,15 @@ 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; public class Start implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage, JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage, - MultitenancyStorage, DashboardSQLStorage, TOTPSQLStorage, ActiveUsersStorage { + UserIdMappingSQLStorage, MultitenancyStorage, MultitenancySQLStorage, DashboardSQLStorage, TOTPSQLStorage, ActiveUsersStorage, AuthRecipeSQLStorage { // 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. @@ -146,7 +152,8 @@ public STORAGE_TYPE getType() { } @Override - public void loadConfig(JsonObject configJson, Set logLevels, TenantIdentifier tenantIdentifier) throws InvalidConfigException { + public void loadConfig(JsonObject configJson, Set logLevels, TenantIdentifier tenantIdentifier) + throws InvalidConfigException { Config.loadConfig(this, configJson, logLevels, tenantIdentifier); } @@ -649,6 +656,17 @@ public void updateSessionInfo_Transaction(TenantIdentifier tenantIdentifier, Tra } } + @Override + public void deleteSessionsOfUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) + throws StorageQueryException { + Connection sqlCon = (Connection) con.getConnection(); + try { + SessionQueries.deleteSessionsOfUser_Transaction(sqlCon, this, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public void setKeyValue_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String key, KeyValueInfo info) @@ -749,7 +767,8 @@ public boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, Str @TestOnly @Override - public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifier, String className, String userId) throws StorageQueryException { + public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifier, String className, String userId) + throws StorageQueryException { if (!isTesting) { throw new UnsupportedOperationException("This method is only for testing"); } @@ -818,7 +837,8 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi try { long now = System.currentTimeMillis(); TOTPQueries.insertUsedCode_Transaction(this, - (Connection) con.getConnection(), tenantIdentifier, new TOTPUsedCode(userId, "123456", true, 1000+now, now)); + (Connection) con.getConnection(), tenantIdentifier, + new TOTPUsedCode(userId, "123456", true, 1000 + now, now)); } catch (SQLException e) { throw new StorageTransactionLogicException(e); } @@ -852,7 +872,8 @@ public String[] getProtectedConfigsFromSuperTokensSaaSUsers() { } @Override - public UserInfo signUp(TenantIdentifier tenantIdentifier, String id, String email, String passwordHash, long timeJoined) + public AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, String email, String passwordHash, + long timeJoined) throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, TenantOrAppNotFoundException { try { @@ -885,28 +906,12 @@ public UserInfo signUp(TenantIdentifier tenantIdentifier, String id, String emai } @Override - public void deleteEmailPasswordUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException { - try { - EmailPasswordQueries.deleteUser(this, appIdentifier, userId); - } catch (StorageTransactionLogicException e) { - throw new StorageQueryException(e.actualException); - } - } - - @Override - public UserInfo getUserInfoUsingId(AppIdentifier appIdentifier, String id) throws StorageQueryException { - try { - return EmailPasswordQueries.getUserInfoUsingId(this, appIdentifier, id); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public UserInfo getUserInfoUsingEmail(TenantIdentifier tenantIdentifier, String email) + public void deleteEmailPasswordUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + String userId, boolean deleteUserIdMappingToo) throws StorageQueryException { try { - return EmailPasswordQueries.getUserInfoUsingEmail(this, tenantIdentifier, email); + Connection sqlCon = (Connection) con.getConnection(); + EmailPasswordQueries.deleteUser_Transaction(sqlCon, this, appIdentifier, userId, deleteUserIdMappingToo); } catch (SQLException e) { throw new StorageQueryException(e); } @@ -917,7 +922,7 @@ public void addPasswordResetToken(AppIdentifier appIdentifier, PasswordResetToke throws StorageQueryException, UnknownUserIdException, DuplicatePasswordResetTokenException { try { EmailPasswordQueries.addPasswordResetToken(this, appIdentifier, passwordResetTokenInfo.userId, - passwordResetTokenInfo.token, passwordResetTokenInfo.tokenExpiry); + passwordResetTokenInfo.token, passwordResetTokenInfo.tokenExpiry, passwordResetTokenInfo.email); } catch (SQLException e) { if (e instanceof PSQLException) { ServerErrorMessage serverMessage = ((PSQLException) e).getServerErrorMessage(); @@ -1009,18 +1014,6 @@ public void updateUsersEmail_Transaction(AppIdentifier appIdentifier, Transactio } } - @Override - public UserInfo getUserInfoUsingId_Transaction(AppIdentifier appIdentifier, TransactionConnection con, - String userId) - throws StorageQueryException { - Connection sqlCon = (Connection) con.getConnection(); - try { - return EmailPasswordQueries.getUserInfoUsingId_Transaction(this, sqlCon, appIdentifier, userId); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - @Override public void deleteExpiredEmailVerificationTokens() throws StorageQueryException { try { @@ -1090,12 +1083,14 @@ public void updateIsEmailVerified_Transaction(AppIdentifier appIdentifier, Trans } @Override - public void deleteEmailVerificationUserInfo(AppIdentifier appIdentifier, String userId) + public void deleteEmailVerificationUserInfo_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + String userId) throws StorageQueryException { try { - EmailVerificationQueries.deleteUserInfo(this, appIdentifier, userId); - } catch (StorageTransactionLogicException e) { - throw new StorageQueryException(e.actualException); + Connection sqlCon = (Connection) con.getConnection(); + EmailVerificationQueries.deleteUserInfo_Transaction(sqlCon, this, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); } } @@ -1196,21 +1191,6 @@ public void deleteExpiredPasswordResetTokens() throws StorageQueryException { } } - @Override - public io.supertokens.pluginInterface.thirdparty.UserInfo getUserInfoUsingId_Transaction( - AppIdentifier appIdentifier, TransactionConnection con, - String thirdPartyId, - String thirdPartyUserId) - throws StorageQueryException { - Connection sqlCon = (Connection) con.getConnection(); - try { - return ThirdPartyQueries.getUserInfoUsingId_Transaction(this, sqlCon, appIdentifier, thirdPartyId, - thirdPartyUserId); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - @Override public void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String thirdPartyId, String thirdPartyUserId, @@ -1225,9 +1205,9 @@ public void updateUserEmail_Transaction(AppIdentifier appIdentifier, Transaction } @Override - public io.supertokens.pluginInterface.thirdparty.UserInfo signUp( + public AuthRecipeUserInfo signUp( TenantIdentifier tenantIdentifier, String id, String email, - io.supertokens.pluginInterface.thirdparty.UserInfo.ThirdParty thirdParty, long timeJoined) + LoginMethod.ThirdParty thirdParty, long timeJoined) throws StorageQueryException, io.supertokens.pluginInterface.thirdparty.exception.DuplicateUserIdException, DuplicateThirdPartyUserException, TenantOrAppNotFoundException { try { @@ -1268,44 +1248,12 @@ public io.supertokens.pluginInterface.thirdparty.UserInfo signUp( } @Override - public void deleteThirdPartyUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException { - try { - ThirdPartyQueries.deleteUser(this, appIdentifier, userId); - } catch (StorageTransactionLogicException e) { - throw new StorageQueryException(e.actualException); - } - } - - @Override - public io.supertokens.pluginInterface.thirdparty.UserInfo getThirdPartyUserInfoUsingId( - TenantIdentifier tenantIdentifier, String thirdPartyId, - String thirdPartyUserId) + public void deleteThirdPartyUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, + boolean deleteUserIdMappingToo) throws StorageQueryException { try { - return ThirdPartyQueries.getThirdPartyUserInfoUsingId(this, tenantIdentifier, thirdPartyId, - thirdPartyUserId); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public io.supertokens.pluginInterface.thirdparty.UserInfo getThirdPartyUserInfoUsingId(AppIdentifier appIdentifier, - String id) - throws StorageQueryException { - try { - return ThirdPartyQueries.getThirdPartyUserInfoUsingId(this, appIdentifier, id); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public io.supertokens.pluginInterface.thirdparty.UserInfo[] getThirdPartyUsersByEmail( - TenantIdentifier tenantIdentifier, @NotNull String email) - throws StorageQueryException { - try { - return ThirdPartyQueries.getThirdPartyUsersByEmail(this, tenantIdentifier, email); + Connection sqlCon = (Connection) con.getConnection(); + ThirdPartyQueries.deleteUser_Transaction(sqlCon, this, appIdentifier, userId, deleteUserIdMappingToo); } catch (SQLException e) { throw new StorageQueryException(e); } @@ -1392,9 +1340,11 @@ public int countUsersEnabledTotpAndActiveSince(AppIdentifier appIdentifier, long } @Override - public void deleteUserActive(AppIdentifier appIdentifier, String userId) throws StorageQueryException { + public void deleteUserActive_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) + throws StorageQueryException { try { - ActiveUsersQueries.deleteUserActive(this, appIdentifier, userId); + Connection sqlCon = (Connection) con.getConnection(); + ActiveUsersQueries.deleteUserActive_Transaction(sqlCon, this, appIdentifier, userId); } catch (SQLException e) { throw new StorageQueryException(e); } @@ -1410,6 +1360,57 @@ public boolean doesUserIdExist(TenantIdentifier tenantIdentifier, String userId) } } + @Override + public AuthRecipeUserInfo getPrimaryUserById(AppIdentifier appIdentifier, String userId) + throws StorageQueryException { + try { + return GeneralQueries.getPrimaryUserInfoForUserId(this, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public String getPrimaryUserIdStrForUserId(AppIdentifier appIdentifier, String userId) + throws StorageQueryException { + try { + return GeneralQueries.getPrimaryUserIdStrForUserId(this, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public AuthRecipeUserInfo[] listPrimaryUsersByEmail(TenantIdentifier tenantIdentifier, String email) + throws StorageQueryException { + try { + return GeneralQueries.listPrimaryUsersByEmail(this, tenantIdentifier, email); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber(TenantIdentifier tenantIdentifier, String phoneNumber) + throws StorageQueryException { + try { + return GeneralQueries.listPrimaryUsersByPhoneNumber(this, tenantIdentifier, phoneNumber); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(TenantIdentifier tenantIdentifier, String thirdPartyId, + String thirdPartyUserId) throws StorageQueryException { + try { + return GeneralQueries.getPrimaryUserByThirdPartyInfo(this, tenantIdentifier, thirdPartyId, + thirdPartyUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public List getJWTSigningKeys_Transaction(AppIdentifier appIdentifier, TransactionConnection con) @@ -1598,7 +1599,8 @@ public void deleteCode_Transaction(TenantIdentifier tenantIdentifier, Transactio } @Override - public void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, String email) + public void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, + String email) throws StorageQueryException, UnknownUserIdException, DuplicateEmailException { Connection sqlCon = (Connection) con.getConnection(); try { @@ -1716,9 +1718,11 @@ public void createCode(TenantIdentifier tenantIdentifier, PasswordlessCode code) } @Override - public io.supertokens.pluginInterface.passwordless.UserInfo createUser(TenantIdentifier tenantIdentifier, - String id, @javax.annotation.Nullable String email, - @javax.annotation.Nullable String phoneNumber, long timeJoined) + public AuthRecipeUserInfo createUser(TenantIdentifier tenantIdentifier, + String id, + @javax.annotation.Nullable String email, + @javax.annotation.Nullable + String phoneNumber, long timeJoined) throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, DuplicateUserIdException, TenantOrAppNotFoundException { @@ -1772,12 +1776,14 @@ public io.supertokens.pluginInterface.passwordless.UserInfo createUser(TenantIde } @Override - public void deletePasswordlessUser(AppIdentifier appIdentifier, String userId) throws + public void deletePasswordlessUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + String userId, boolean deleteUserIdMappingToo) throws StorageQueryException { try { - PasswordlessQueries.deleteUser(this, appIdentifier, userId); - } catch (StorageTransactionLogicException e) { - throw new StorageQueryException(e.actualException); + Connection sqlCon = (Connection) con.getConnection(); + PasswordlessQueries.deleteUser_Transaction(sqlCon, this, appIdentifier, userId, deleteUserIdMappingToo); + } catch (SQLException e) { + throw new StorageQueryException(e); } } @@ -1852,37 +1858,6 @@ public PasswordlessCode getCodeByLinkCodeHash(TenantIdentifier tenantIdentifier, } } - @Override - public io.supertokens.pluginInterface.passwordless.UserInfo getUserById(AppIdentifier appIdentifier, - String userId) - throws StorageQueryException { - try { - return PasswordlessQueries.getUserById(this, appIdentifier, userId); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public io.supertokens.pluginInterface.passwordless.UserInfo getUserByEmail(TenantIdentifier tenantIdentifier, String email) - throws StorageQueryException { - try { - return PasswordlessQueries.getUserByEmail(this, tenantIdentifier, email); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - - @Override - public io.supertokens.pluginInterface.passwordless.UserInfo getUserByPhoneNumber(TenantIdentifier tenantIdentifier, String phoneNumber) - throws StorageQueryException { - try { - return PasswordlessQueries.getUserByPhoneNumber(this, tenantIdentifier, phoneNumber); - } catch (SQLException e) { - throw new StorageQueryException(e); - } - } - @Override public JsonObject getUserMetadata(AppIdentifier appIdentifier, String userId) throws StorageQueryException { try { @@ -1905,7 +1880,8 @@ public JsonObject getUserMetadata_Transaction(AppIdentifier appIdentifier, Trans } @Override - public int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, JsonObject metadata) + public int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, + JsonObject metadata) throws StorageQueryException, TenantOrAppNotFoundException { Connection sqlCon = (Connection) con.getConnection(); try { @@ -1924,6 +1900,17 @@ public int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionC } } + @Override + public int deleteUserMetadata_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return UserMetadataQueries.deleteUserMetadata_Transaction(sqlCon, this, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @Override public int deleteUserMetadata(AppIdentifier appIdentifier, String userId) throws StorageQueryException { try { @@ -2046,17 +2033,20 @@ public int deleteAllRolesForUser(TenantIdentifier tenantIdentifier, String userI } @Override - public void deleteAllRolesForUser(AppIdentifier appIdentifier, String userId) throws + public void deleteAllRolesForUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) + throws StorageQueryException { try { - UserRolesQueries.deleteAllRolesForUser(this, appIdentifier, userId); + Connection sqlCon = (Connection) con.getConnection(); + UserRolesQueries.deleteAllRolesForUser_Transaction(sqlCon, this, appIdentifier, userId); } catch (SQLException e) { throw new StorageQueryException(e); } } @Override - public boolean deleteRoleForUser_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId, String role) + public boolean deleteRoleForUser_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String userId, String role) throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); @@ -2114,7 +2104,8 @@ public void addPermissionToRoleOrDoNothingIfExists_Transaction(AppIdentifier app } @Override - public boolean deletePermissionForRole_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role, String permission) + public boolean deletePermissionForRole_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String role, String permission) throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); try { @@ -2126,7 +2117,8 @@ public boolean deletePermissionForRole_Transaction(AppIdentifier appIdentifier, } @Override - public int deleteAllPermissionsForRole_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) + public int deleteAllPermissionsForRole_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String role) throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); try { @@ -2138,7 +2130,8 @@ public int deleteAllPermissionsForRole_Transaction(AppIdentifier appIdentifier, } @Override - public boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) throws StorageQueryException { + public boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) + throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); try { return UserRolesQueries.doesRoleExist_transaction(this, sqlCon, appIdentifier, role); @@ -2183,7 +2176,8 @@ public void createUserIdMapping(AppIdentifier appIdentifier, String superTokensU } @Override - public boolean deleteUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) throws StorageQueryException { + public boolean deleteUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) + throws StorageQueryException { try { if (isSuperTokensUserId) { return UserIdMappingQueries.deleteUserIdMappingWithSuperTokensUserId(this, appIdentifier, @@ -2197,7 +2191,8 @@ public boolean deleteUserIdMapping(AppIdentifier appIdentifier, String userId, b } @Override - public UserIdMapping getUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) throws StorageQueryException { + public UserIdMapping getUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) + throws StorageQueryException { try { if (isSuperTokensUserId) { return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId(this, appIdentifier, @@ -2332,6 +2327,7 @@ public boolean deleteTenantInfoInBaseStorage(TenantIdentifier tenantIdentifier) public boolean deleteAppInfoInBaseStorage(AppIdentifier appIdentifier) throws StorageQueryException { return deleteTenantInfoInBaseStorage(appIdentifier.getAsPublicTenantIdentifier()); } + @Override public boolean deleteConnectionUriDomainInfoInBaseStorage(String connectionUriDomain) throws StorageQueryException { return deleteTenantInfoInBaseStorage(new TenantIdentifier(connectionUriDomain, null, null)); @@ -2343,67 +2339,57 @@ public TenantConfig[] getAllTenants() throws StorageQueryException { } @Override - public boolean addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) + public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId) throws TenantOrAppNotFoundException, UnknownUserIdException, StorageQueryException, DuplicateEmailException, DuplicateThirdPartyUserException, DuplicatePhoneNumberException { + Connection sqlCon = (Connection) con.getConnection(); try { - return this.startTransaction(con -> { - Connection sqlCon = (Connection) con.getConnection(); - try { - String recipeId = GeneralQueries.getRecipeIdForUser_Transaction(this, sqlCon, tenantIdentifier, - userId); - - if (recipeId == null) { - throw new StorageTransactionLogicException(new UnknownUserIdException()); - } + String recipeId = GeneralQueries.getRecipeIdForUser_Transaction(this, sqlCon, tenantIdentifier, + userId); - boolean added; - if (recipeId.equals("emailpassword")) { - added = EmailPasswordQueries.addUserIdToTenant_Transaction(this, sqlCon, tenantIdentifier, userId); - } else if (recipeId.equals("thirdparty")) { - added = ThirdPartyQueries.addUserIdToTenant_Transaction(this, sqlCon, tenantIdentifier, userId); - } else if (recipeId.equals("passwordless")) { - added = PasswordlessQueries.addUserIdToTenant_Transaction(this, sqlCon, tenantIdentifier, userId); - } else { - throw new IllegalStateException("Should never come here!"); - } + if (recipeId == null) { + throw new UnknownUserIdException(); + } - sqlCon.commit(); - return added; - } catch (SQLException throwables) { - throw new StorageTransactionLogicException(throwables); - } - }); - } catch (StorageTransactionLogicException e) { - if (e.actualException instanceof SQLException) { - PostgreSQLConfig config = Config.getConfig(this); - ServerErrorMessage serverErrorMessage = ((PSQLException) e.actualException).getServerErrorMessage(); + boolean added; + if (recipeId.equals("emailpassword")) { + added = EmailPasswordQueries.addUserIdToTenant_Transaction(this, sqlCon, tenantIdentifier, + userId); + } else if (recipeId.equals("thirdparty")) { + added = ThirdPartyQueries.addUserIdToTenant_Transaction(this, sqlCon, tenantIdentifier, userId); + } else if (recipeId.equals("passwordless")) { + added = PasswordlessQueries.addUserIdToTenant_Transaction(this, sqlCon, tenantIdentifier, + userId); + } else { + throw new IllegalStateException("Should never come here!"); + } - if (isForeignKeyConstraintError(serverErrorMessage, config.getUsersTable(), "tenant_id")) { - throw new TenantOrAppNotFoundException(tenantIdentifier); - } - if (isUniqueConstraintError(serverErrorMessage, config.getEmailPasswordUserToTenantTable(), "email")) { - throw new DuplicateEmailException(); - } - if (isUniqueConstraintError(serverErrorMessage, config.getThirdPartyUserToTenantTable(), "third_party_user_id")) { - throw new DuplicateThirdPartyUserException(); - } - if (isUniqueConstraintError(serverErrorMessage, - Config.getConfig(this).getPasswordlessUserToTenantTable(), "phone_number")) { - throw new DuplicatePhoneNumberException(); - } - if (isUniqueConstraintError(serverErrorMessage, - Config.getConfig(this).getPasswordlessUserToTenantTable(), "email")) { - throw new DuplicateEmailException(); - } + sqlCon.commit(); + return added; + } catch (SQLException throwables) { + PostgreSQLConfig config = Config.getConfig(this); + ServerErrorMessage serverErrorMessage = ((PSQLException) throwables).getServerErrorMessage(); - throw new StorageQueryException(e.actualException); - } else if (e.actualException instanceof UnknownUserIdException) { - throw (UnknownUserIdException) e.actualException; - } else if (e.actualException instanceof StorageQueryException) { - throw (StorageQueryException) e.actualException; + if (isForeignKeyConstraintError(serverErrorMessage, config.getUsersTable(), "tenant_id")) { + throw new TenantOrAppNotFoundException(tenantIdentifier); } - throw new StorageQueryException(e.actualException); + if (isUniqueConstraintError(serverErrorMessage, config.getEmailPasswordUserToTenantTable(), "email")) { + throw new DuplicateEmailException(); + } + if (isUniqueConstraintError(serverErrorMessage, config.getThirdPartyUserToTenantTable(), + "third_party_user_id")) { + throw new DuplicateThirdPartyUserException(); + } + if (isUniqueConstraintError(serverErrorMessage, + Config.getConfig(this).getPasswordlessUserToTenantTable(), "phone_number")) { + throw new DuplicatePhoneNumberException(); + } + if (isUniqueConstraintError(serverErrorMessage, + Config.getConfig(this).getPasswordlessUserToTenantTable(), "email")) { + throw new DuplicateEmailException(); + } + + throw new StorageQueryException(throwables); } } @@ -2424,11 +2410,14 @@ public boolean removeUserIdFromTenant(TenantIdentifier tenantIdentifier, String boolean removed; if (recipeId.equals("emailpassword")) { - removed = EmailPasswordQueries.removeUserIdFromTenant_Transaction(this, sqlCon, tenantIdentifier, userId); + removed = EmailPasswordQueries.removeUserIdFromTenant_Transaction(this, sqlCon, + tenantIdentifier, userId); } else if (recipeId.equals("thirdparty")) { - removed = ThirdPartyQueries.removeUserIdFromTenant_Transaction(this, sqlCon, tenantIdentifier, userId); + removed = ThirdPartyQueries.removeUserIdFromTenant_Transaction(this, sqlCon, tenantIdentifier, + userId); } else if (recipeId.equals("passwordless")) { - removed = PasswordlessQueries.removeUserIdFromTenant_Transaction(this, sqlCon, tenantIdentifier, userId); + removed = PasswordlessQueries.removeUserIdFromTenant_Transaction(this, sqlCon, tenantIdentifier, + userId); } else { throw new IllegalStateException("Should never come here!"); } @@ -2446,11 +2435,12 @@ public boolean removeUserIdFromTenant(TenantIdentifier tenantIdentifier, String throw (StorageQueryException) e.actualException; } throw new StorageQueryException(e.actualException); - } + } } @Override - public boolean deleteDashboardUserWithUserId(AppIdentifier appIdentifier, String userId) throws StorageQueryException { + public boolean deleteDashboardUserWithUserId(AppIdentifier appIdentifier, String userId) + throws StorageQueryException { try { return DashboardQueries.deleteDashboardUserWithUserId(this, appIdentifier, userId); } catch (SQLException e) { @@ -2500,7 +2490,8 @@ public DashboardSessionInfo getSessionInfoWithSessionId(AppIdentifier appIdentif } @Override - public boolean revokeSessionWithSessionId(AppIdentifier appIdentifier, String sessionId) throws StorageQueryException { + public boolean revokeSessionWithSessionId(AppIdentifier appIdentifier, String sessionId) + throws StorageQueryException { try { return DashboardQueries.deleteDashboardUserSessionWithSessionId(this, appIdentifier, sessionId); @@ -2510,7 +2501,8 @@ public boolean revokeSessionWithSessionId(AppIdentifier appIdentifier, String se } @Override - public void updateDashboardUsersEmailWithUserId_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, + public void updateDashboardUsersEmailWithUserId_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String userId, String newEmail) throws StorageQueryException, io.supertokens.pluginInterface.dashboard.exceptions.DuplicateEmailException, UserIdNotFoundException { Connection sqlCon = (Connection) con.getConnection(); @@ -2815,8 +2807,182 @@ public String[] getAllTablesInTheDatabaseThatHasDataForAppId(String appId) throw } } + @Override + public AuthRecipeUserInfo getPrimaryUserById_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String userId) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return GeneralQueries.getPrimaryUserInfoForUserId_Transaction(this, sqlCon, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, String email) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return GeneralQueries.listPrimaryUsersByEmail_Transaction(this, sqlCon, appIdentifier, email); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, + String phoneNumber) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return GeneralQueries.listPrimaryUsersByPhoneNumber_Transaction(this, sqlCon, appIdentifier, + phoneNumber); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo(AppIdentifier appIdentifier, + String thirdPartyId, + String thirdPartyUserId) + throws StorageQueryException { + try { + return GeneralQueries.listPrimaryUsersByThirdPartyInfo(this, appIdentifier, + thirdPartyId, thirdPartyUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, + String thirdPartyId, + String thirdPartyUserId) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return GeneralQueries.listPrimaryUsersByThirdPartyInfo_Transaction(this, sqlCon, appIdentifier, + thirdPartyId, thirdPartyUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void makePrimaryUser_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + // we do not bother returning if a row was updated here or not, cause it's happening + // in a transaction anyway. + GeneralQueries.makePrimaryUser_Transaction(this, sqlCon, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void linkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String recipeUserId, + String primaryUserId) throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + // we do not bother returning if a row was updated here or not, cause it's happening + // in a transaction anyway. + GeneralQueries.linkAccounts_Transaction(this, sqlCon, appIdentifier, recipeUserId, primaryUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, String recipeUserId) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + // we do not bother returning if a row was updated here or not, cause it's happening + // in a transaction anyway. + GeneralQueries.unlinkAccounts_Transaction(this, sqlCon, appIdentifier, primaryUserId, recipeUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean doesUserIdExist_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + String externalUserId) throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return GeneralQueries.doesUserIdExist_Transaction(this, sqlCon, appIdentifier, externalUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public boolean checkIfUsesAccountLinking(AppIdentifier appIdentifier) throws StorageQueryException { + try { + return GeneralQueries.checkIfUsesAccountLinking(this, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException { + try { + return ActiveUsersQueries.countUsersActiveSinceAndHasMoreThanOneLoginMethod(this, appIdentifier, sinceTime); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public int getUsersCountWithMoreThanOneLoginMethod(AppIdentifier appIdentifier) throws StorageQueryException { + try { + return GeneralQueries.getUsersCountWithMoreThanOneLoginMethod(this, appIdentifier); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + @TestOnly public Thread getMainThread() { return mainThread; } + + @Override + public UserIdMapping getUserIdMapping_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + String userId, boolean isSuperTokensUserId) + throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + if (isSuperTokensUserId) { + return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId_Transaction(this, sqlCon, appIdentifier, + userId); + } + + return UserIdMappingQueries.getUserIdMappingWithExternalUserId_Transaction(this, sqlCon, appIdentifier, userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } + + @Override + public UserIdMapping[] getUserIdMapping_Transaction(TransactionConnection con, AppIdentifier appIdentifier, + String userId) throws StorageQueryException { + try { + Connection sqlCon = (Connection) con.getConnection(); + return UserIdMappingQueries.getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(this, + sqlCon, + appIdentifier, + userId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } + } } 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 52508166..d40c08f1 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java @@ -1,14 +1,14 @@ package io.supertokens.storage.postgresql.queries; -import java.math.BigInteger; -import java.sql.SQLException; - +import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.storage.postgresql.Start; -import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; +import java.sql.Connection; +import java.sql.SQLException; + import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -21,9 +21,10 @@ static String getQueryToCreateUserLastActiveTable(Start start) { + "user_id VARCHAR(128)," + "last_active_time BIGINT," + "PRIMARY KEY(app_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, Config.getConfig(start).getUserLastActiveTable(), "app_id", "fkey") + + "CONSTRAINT " + + Utils.getConstraintName(schema, Config.getConfig(start).getUserLastActiveTable(), "app_id", "fkey") + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; } @@ -32,7 +33,8 @@ static String getQueryToCreateAppIdIndexForUserLastActiveTable(Start start) { + Config.getConfig(start).getUserLastActiveTable() + "(app_id);"; } - public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) throws SQLException, StorageQueryException { + public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) + throws SQLException, StorageQueryException { String QUERY = "SELECT COUNT(*) as total FROM " + Config.getConfig(start).getUserLastActiveTable() + " WHERE app_id = ? AND last_active_time >= ?"; @@ -47,7 +49,30 @@ public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier }); } - public static int countUsersEnabledTotp(Start start, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { + public static int countUsersActiveSinceAndHasMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier, long sinceTime) + throws SQLException, StorageQueryException { + String QUERY = "SELECT count(1) as c FROM (" + + " SELECT count(user_id) as num_login_methods, app_id, primary_or_recipe_user_id" + + " FROM " + Config.getConfig(start).getUsersTable() + + " WHERE primary_or_recipe_user_id IN (" + + " SELECT user_id FROM " + Config.getConfig(start).getUserLastActiveTable() + + " WHERE app_id = ? AND last_active_time >= ?" + + " )" + + " GROUP BY app_id, primary_or_recipe_user_id" + + ") uc WHERE num_login_methods > 1"; + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setLong(2, sinceTime); + }, result -> { + if (result.next()) { + return result.getInt("c"); + } + return 0; + }); + } + + public static int countUsersEnabledTotp(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { String QUERY = "SELECT COUNT(*) as total FROM " + Config.getConfig(start).getTotpUsersTable() + " WHERE app_id = ?"; @@ -61,11 +86,13 @@ public static int countUsersEnabledTotp(Start start, AppIdentifier appIdentifier }); } - public static int countUsersEnabledTotpAndActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) throws SQLException, StorageQueryException { - String QUERY = "SELECT COUNT(*) as total FROM " + Config.getConfig(start).getTotpUsersTable() + " AS totp_users " - + "INNER JOIN " + Config.getConfig(start).getUserLastActiveTable() + " AS user_last_active " - + "ON totp_users.user_id = user_last_active.user_id " - + "WHERE user_last_active.app_id = ? AND user_last_active.last_active_time >= ?"; + public static int countUsersEnabledTotpAndActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) + throws SQLException, StorageQueryException { + String QUERY = + "SELECT COUNT(*) as total FROM " + Config.getConfig(start).getTotpUsersTable() + " AS totp_users " + + "INNER JOIN " + Config.getConfig(start).getUserLastActiveTable() + " AS user_last_active " + + "ON totp_users.user_id = user_last_active.user_id " + + "WHERE user_last_active.app_id = ? AND user_last_active.last_active_time >= ?"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -78,9 +105,12 @@ public static int countUsersEnabledTotpAndActiveSince(Start start, AppIdentifier }); } - public static int updateUserLastActive(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { + public static int updateUserLastActive(Start start, AppIdentifier appIdentifier, String userId) + 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 = ?"; + + + "(app_id, user_id, last_active_time) VALUES(?, ?, ?) ON CONFLICT(app_id, user_id) DO UPDATE SET " + + "last_active_time = ?"; long now = System.currentTimeMillis(); return update(start, QUERY, pst -> { @@ -111,12 +141,13 @@ public static Long getLastActiveByUserId(Start start, AppIdentifier appIdentifie } } - public static void deleteUserActive(Start start, AppIdentifier appIdentifier, String userId) + public static void deleteUserActive_Transaction(Connection con, Start start, AppIdentifier appIdentifier, + String userId) throws StorageQueryException, SQLException { String QUERY = "DELETE FROM " + Config.getConfig(start).getUserLastActiveTable() + " WHERE app_id = ? AND user_id = ?"; - update(start, QUERY, pst -> { + update(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); pst.setString(2, userId); }); 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 8b893162..55bb51c4 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java @@ -17,8 +17,11 @@ package io.supertokens.storage.postgresql.queries; import io.supertokens.pluginInterface.RowMapper; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; +import io.supertokens.pluginInterface.authRecipe.LoginMethod; import io.supertokens.pluginInterface.emailpassword.PasswordResetTokenInfo; -import io.supertokens.pluginInterface.emailpassword.UserInfo; +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; @@ -32,6 +35,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.stream.Collectors; import static io.supertokens.pluginInterface.RECIPE_ID.EMAIL_PASSWORD; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; @@ -52,7 +56,8 @@ static String getQueryToCreateUsersTable(Start start) { + "time_joined BIGINT NOT NULL," + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, "user_id", "fkey") + " FOREIGN KEY(app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE," + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + + " (app_id, user_id) ON DELETE CASCADE," + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, null, "pkey") + " PRIMARY KEY (app_id, user_id)" + ");"; @@ -74,7 +79,8 @@ static String getQueryToCreateEmailPasswordUserToTenantTable(Start start) { + " PRIMARY KEY (app_id, tenant_id, user_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, "user_id", "fkey") + " FOREIGN KEY (app_id, tenant_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getUsersTable() + "(app_id, tenant_id, user_id) ON DELETE CASCADE" + + " REFERENCES " + Config.getConfig(start).getUsersTable() + + "(app_id, tenant_id, user_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -87,13 +93,15 @@ static String getQueryToCreatePasswordResetTokensTable(Start start) { + "app_id VARCHAR(64) DEFAULT 'public'," + "user_id CHAR(36) NOT NULL," + "token VARCHAR(128) NOT NULL" - + " CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "token", "key") + " UNIQUE," + + " CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "token", "key") + + " UNIQUE," + + "email VARCHAR(256)," // nullable cause of backwards compatibility. + "token_expiry BIGINT NOT NULL," + "CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, null, "pkey") + " PRIMARY KEY (app_id, user_id, token)," + "CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "user_id", "fkey") + " FOREIGN KEY (app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getEmailPasswordUsersTable() + "(app_id, user_id)" + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id, user_id)" + " ON DELETE CASCADE ON UPDATE CASCADE" + ");"; // @formatter:on @@ -115,7 +123,8 @@ public static void deleteExpiredPasswordResetTokens(Start start) throws SQLExcep update(start, QUERY, pst -> pst.setLong(1, currentTimeMillis())); } - public static void updateUsersPassword_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String newPassword) + public static void updateUsersPassword_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String userId, String newPassword) throws SQLException, StorageQueryException { String QUERY = "UPDATE " + getConfig(start).getEmailPasswordUsersTable() + " SET password_hash = ? WHERE app_id = ? AND user_id = ?"; @@ -127,7 +136,8 @@ public static void updateUsersPassword_Transaction(Start start, Connection con, }); } - public static void updateUsersEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String newEmail) + public static void updateUsersEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String userId, String newEmail) throws SQLException, StorageQueryException { { String QUERY = "UPDATE " + getConfig(start).getEmailPasswordUsersTable() @@ -151,10 +161,12 @@ public static void updateUsersEmail_Transaction(Start start, Connection con, App } } - public static void deleteAllPasswordResetTokensForUser_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) + public static void deleteAllPasswordResetTokensForUser_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { - String QUERY = "DELETE FROM " + getConfig(start).getPasswordResetTokensTable() + " WHERE app_id = ? AND user_id = ?"; + String QUERY = + "DELETE FROM " + getConfig(start).getPasswordResetTokensTable() + " WHERE app_id = ? AND user_id = ?"; update(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -165,8 +177,9 @@ public static void deleteAllPasswordResetTokensForUser_Transaction(Start start, public static PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser(Start start, AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { - String QUERY = "SELECT user_id, token, token_expiry FROM " + getConfig(start).getPasswordResetTokensTable() - + " WHERE app_id = ? AND user_id = ?"; + String QUERY = + "SELECT user_id, token, token_expiry, email FROM " + getConfig(start).getPasswordResetTokensTable() + + " WHERE app_id = ? AND user_id = ?"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -189,8 +202,9 @@ public static PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser_Trans String userId) throws SQLException, StorageQueryException { - String QUERY = "SELECT user_id, token, token_expiry FROM " + getConfig(start).getPasswordResetTokensTable() - + " WHERE app_id = ? AND user_id = ? FOR UPDATE"; + String QUERY = + "SELECT user_id, token, token_expiry, email FROM " + getConfig(start).getPasswordResetTokensTable() + + " WHERE app_id = ? AND user_id = ? FOR UPDATE"; return execute(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); @@ -208,28 +222,12 @@ public static PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser_Trans }); } - public static UserInfo getUserInfoUsingId_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - String id) + public static PasswordResetTokenInfo getPasswordResetTokenInfo(Start start, AppIdentifier appIdentifier, + String token) throws SQLException, StorageQueryException { - String QUERY = "SELECT user_id, email, password_hash, time_joined FROM " - + getConfig(start).getEmailPasswordUsersTable() - + " WHERE app_id = ? AND user_id = ? FOR UPDATE"; - UserInfoPartial userInfo = execute(con, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, id); - }, result -> { - if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); - } - return null; - }); - return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfo); - } - - public static PasswordResetTokenInfo getPasswordResetTokenInfo(Start start, AppIdentifier appIdentifier, String token) - throws SQLException, StorageQueryException { - String QUERY = "SELECT user_id, token, token_expiry FROM " + getConfig(start).getPasswordResetTokensTable() - + " WHERE app_id = ? AND token = ?"; + String QUERY = + "SELECT user_id, token, token_expiry, email FROM " + getConfig(start).getPasswordResetTokensTable() + + " WHERE app_id = ? AND token = ?"; return execute(start, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); pst.setString(2, token); @@ -241,43 +239,62 @@ public static PasswordResetTokenInfo getPasswordResetTokenInfo(Start start, AppI }); } - public static void addPasswordResetToken(Start start, AppIdentifier appIdentifier, String userId, String tokenHash, long expiry) + public static void addPasswordResetToken(Start start, AppIdentifier appIdentifier, String userId, String tokenHash, + long expiry, String email) throws SQLException, StorageQueryException { - String QUERY = "INSERT INTO " + getConfig(start).getPasswordResetTokensTable() - + "(app_id, user_id, token, token_expiry)" + " VALUES(?, ?, ?, ?)"; + if (email != null) { + String QUERY = "INSERT INTO " + getConfig(start).getPasswordResetTokensTable() + + "(app_id, user_id, token, token_expiry, email)" + " VALUES(?, ?, ?, ?, ?)"; - update(start, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); - pst.setString(3, tokenHash); - pst.setLong(4, expiry); - }); + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + pst.setString(3, tokenHash); + pst.setLong(4, expiry); + pst.setString(5, email); + }); + } else { + String QUERY = "INSERT INTO " + getConfig(start).getPasswordResetTokensTable() + + "(app_id, user_id, token, token_expiry)" + " VALUES(?, ?, ?, ?)"; + + update(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + pst.setString(3, tokenHash); + pst.setLong(4, expiry); + }); + } } - public static UserInfo signUp(Start start, TenantIdentifier tenantIdentifier, String userId, String email, String passwordHash, long timeJoined) + public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIdentifier, String userId, String email, + String passwordHash, long timeJoined) throws StorageQueryException, StorageTransactionLogicException { return start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); try { { // app_id_to_user_id String QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() - + "(app_id, user_id, recipe_id)" + " VALUES(?, ?, ?)"; + + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, userId); - pst.setString(3, EMAIL_PASSWORD.toString()); + pst.setString(3, userId); + pst.setString(4, EMAIL_PASSWORD.toString()); }); } { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, recipe_id, time_joined)" + " VALUES(?, ?, ?, ?, ?)"; + + "(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()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, userId); - pst.setString(4, EMAIL_PASSWORD.toString()); - pst.setLong(5, timeJoined); + pst.setString(4, userId); + pst.setString(5, EMAIL_PASSWORD.toString()); + pst.setLong(6, timeJoined); + pst.setLong(7, timeJoined); }); } @@ -306,57 +323,62 @@ public static UserInfo signUp(Start start, TenantIdentifier tenantIdentifier, St }); } - UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), new UserInfoPartial(userId, email, passwordHash, timeJoined)); - + 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 userInfo; + return AuthRecipeUserInfo.create(userId, false, userInfo.toLoginMethod()); } catch (SQLException throwables) { throw new StorageTransactionLogicException(throwables); } }); } - public static void deleteUser(Start start, AppIdentifier appIdentifier, String userId) - throws StorageQueryException, StorageTransactionLogicException { - start.startTransaction(con -> { - Connection sqlCon = (Connection) con.getConnection(); - try { - { - String QUERY = "DELETE FROM " + getConfig(start).getAppIdToUserIdTable() - + " WHERE app_id = ? AND user_id = ?"; - - update(sqlCon, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); - }); - } + public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, + String userId, boolean deleteUserIdMappingToo) + throws StorageQueryException, SQLException { + if (deleteUserIdMappingToo) { + String QUERY = "DELETE FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? AND user_id = ?"; - sqlCon.commit(); - } catch (SQLException throwables) { - throw new StorageTransactionLogicException(throwables); + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } else { + { + String QUERY = "DELETE FROM " + getConfig(start).getUsersTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); } - return null; - }); - } - public static UserInfo getUserInfoUsingId(Start start, AppIdentifier appIdentifier, String id) throws SQLException, StorageQueryException { - String QUERY = "SELECT user_id, email, password_hash, time_joined FROM " - + getConfig(start).getEmailPasswordUsersTable() + " WHERE app_id = ? AND user_id = ?"; + { + String QUERY = "DELETE FROM " + getConfig(start).getEmailPasswordUsersTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } - UserInfoPartial userInfo = execute(start, QUERY.toString(), pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, id); - }, result -> { - if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); + { + String QUERY = "DELETE FROM " + getConfig(start).getPasswordResetTokensTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); } - return null; - }); - return userInfoWithTenantIds(start, appIdentifier, userInfo); + } } - public static UserInfoPartial getUserInfoUsingId(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 + 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 " + getConfig(start).getEmailPasswordUsersTable() + " WHERE app_id = ? AND user_id = ?"; @@ -371,29 +393,55 @@ public static UserInfoPartial getUserInfoUsingId(Start start, Connection sqlCon, }); } - public static List getUsersInfoUsingIdList(Start start, AppIdentifier appIdentifier, List ids) + public static List getUsersInfoUsingIdList(Start start, Set ids, + 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 - StringBuilder QUERY = new StringBuilder("SELECT user_id, email, password_hash, time_joined " - + "FROM " + getConfig(start).getEmailPasswordUsersTable()); - QUERY.append(" WHERE app_id = ? AND user_id IN ("); - for (int i = 0; i < ids.size(); i++) { - - QUERY.append("?"); - if (i != ids.size() - 1) { - // not the last element - QUERY.append(","); + String QUERY = "SELECT user_id, email, password_hash, time_joined " + + "FROM " + getConfig(start).getEmailPasswordUsersTable() + + " WHERE user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(ids.size()) + + " ) AND app_id = ?"; + + List userInfos = execute(start, QUERY, pst -> { + int index = 1; + for (String id : ids) { + pst.setString(index, id); + index++; } - } - QUERY.append(")"); + pst.setString(index, appIdentifier.getAppId()); + }, result -> { + List finalResult = new ArrayList<>(); + while (result.next()) { + finalResult.add(UserInfoRowMapper.getInstance().mapOrThrow(result)); + } + return finalResult; + }); + fillUserInfoWithTenantIds(start, appIdentifier, userInfos); + fillUserInfoWithVerified(start, appIdentifier, userInfos); + return userInfos.stream().map(UserInfoPartial::toLoginMethod) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } - List userInfos = execute(start, QUERY.toString(), pst -> { - pst.setString(1, appIdentifier.getAppId()); - for (int i = 0; i < ids.size(); i++) { - // i+2 cause this starts with 1 and not 0, and 1 is appId - pst.setString(i + 2, ids.get(i)); + public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, + 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 + String QUERY = "SELECT user_id, email, password_hash, time_joined " + + "FROM " + getConfig(start).getEmailPasswordUsersTable() + + " WHERE user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(ids.size()) + + " ) AND app_id = ?"; + + List userInfos = execute(con, QUERY, pst -> { + int index = 1; + for (String id : ids) { + pst.setString(index, id); + index++; } + pst.setString(index, appIdentifier.getAppId()); }, result -> { List finalResult = new ArrayList<>(); while (result.next()) { @@ -401,55 +449,111 @@ public static List getUsersInfoUsingIdList(Start start, AppIdentifier } return finalResult; }); - return userInfoWithTenantIds(start, appIdentifier, userInfos); + fillUserInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); + fillUserInfoWithVerified_transaction(start, con, appIdentifier, userInfos); + return userInfos.stream().map(UserInfoPartial::toLoginMethod) + .collect(Collectors.toList()); } return Collections.emptyList(); } + public static String lockEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String email) + throws StorageQueryException, SQLException { + String QUERY = "SELECT user_id FROM " + getConfig(start).getEmailPasswordUsersTable() + + " WHERE app_id = ? AND email = ? FOR UPDATE"; - public static UserInfo getUserInfoUsingEmail(Start start, TenantIdentifier tenantIdentifier, String email) throws StorageQueryException, SQLException { - String QUERY = "SELECT ep_users_to_tenant.user_id as user_id, ep_users_to_tenant.email as email, " - + "ep_users.password_hash as password_hash, ep_users.time_joined as time_joined " - + "FROM " + getConfig(start).getEmailPasswordUserToTenantTable() + " AS ep_users_to_tenant " - + "JOIN " + getConfig(start).getEmailPasswordUsersTable() + " AS ep_users " - + "ON ep_users.app_id = ep_users_to_tenant.app_id AND ep_users.user_id = ep_users_to_tenant.user_id " - + "WHERE ep_users_to_tenant.app_id = ? AND ep_users_to_tenant.tenant_id = ? AND ep_users_to_tenant.email = ?"; + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, email); + }, result -> { + if (result.next()) { + return result.getString("user_id"); + } + return null; + }); + } + + public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, + 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" + + " JOIN " + getConfig(start).getUsersTable() + " 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.tenant_id = ? AND ep.email = ?"; - UserInfoPartial userInfo = execute(start, QUERY, pst -> { + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, email); }, result -> { if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); + return result.getString("user_id"); } return null; }); - return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); + } + + 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" + + " 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 = ?"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, email); + }, 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 { - UserInfoPartial userInfo = EmailPasswordQueries.getUserInfoUsingId(start, sqlCon, + throws SQLException, StorageQueryException, UnknownUserIdException { + UserInfoPartial userInfo = EmailPasswordQueries.getUserInfoUsingId_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + if (userInfo == null) { + throw new UnknownUserIdException(); + } + + 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, recipe_id, time_joined)" - + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; + + "(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; + update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, userId); - pst.setString(4, EMAIL_PASSWORD.toString()); - pst.setLong(5, userInfo.timeJoined); + pst.setString(4, finalAccountLinkingInfo.primaryUserId); + pst.setBoolean(5, finalAccountLinkingInfo.isLinked); + pst.setString(6, EMAIL_PASSWORD.toString()); + pst.setLong(7, userInfo.timeJoined); + pst.setLong(8, userInfo.timeJoined); }); + + 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 DO NOTHING"; + + " VALUES(?, ?, ?, ?) " + " ON CONFLICT ON CONSTRAINT " + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getEmailPasswordUserToTenantTable(), null, "pkey") + + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -462,7 +566,8 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC } } - public static boolean removeUserIdFromTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) + public static boolean removeUserIdFromTenant_Transaction(Start start, Connection sqlCon, + TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { { // all_auth_recipe_users String QUERY = "DELETE FROM " + getConfig(start).getUsersTable() @@ -478,42 +583,103 @@ public static boolean removeUserIdFromTenant_Transaction(Start start, Connection // automatically deleted from emailpassword_user_to_tenant because of foreign key constraint } - private static UserInfo userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, UserInfoPartial userInfo) + private static UserInfoPartial fillUserInfoWithVerified_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, appIdentifier, Arrays.asList(userInfo)).get(0); + return fillUserInfoWithVerified_transaction(start, sqlCon, appIdentifier, List.of(userInfo)).get(0); + } + + private static List fillUserInfoWithVerified_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userInfos) + throws SQLException, StorageQueryException { + List userIdsAndEmails = new ArrayList<>(); + for (UserInfoPartial userInfo : userInfos) { + userIdsAndEmails.add(new EmailVerificationQueries.UserIdAndEmail(userInfo.id, userInfo.email)); + } + List userIdsThatAreVerified = EmailVerificationQueries.isEmailVerified_transaction(start, sqlCon, + appIdentifier, + userIdsAndEmails); + Set verifiedUserIdsSet = new HashSet<>(userIdsThatAreVerified); + for (UserInfoPartial userInfo : userInfos) { + if (verifiedUserIdsSet.contains(userInfo.id)) { + userInfo.verified = true; + } else { + userInfo.verified = false; + } } + return userInfos; } - private static List userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, List userInfos) + private static List fillUserInfoWithVerified(Start start, + AppIdentifier appIdentifier, + List userInfos) throws SQLException, StorageQueryException { - try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); + List userIdsAndEmails = new ArrayList<>(); + for (UserInfoPartial userInfo : userInfos) { + userIdsAndEmails.add(new EmailVerificationQueries.UserIdAndEmail(userInfo.id, userInfo.email)); } + List userIdsThatAreVerified = EmailVerificationQueries.isEmailVerified(start, + appIdentifier, + userIdsAndEmails); + Set verifiedUserIdsSet = new HashSet<>(userIdsThatAreVerified); + for (UserInfoPartial userInfo : userInfos) { + if (verifiedUserIdsSet.contains(userInfo.id)) { + userInfo.verified = true; + } else { + userInfo.verified = false; + } + } + return userInfos; } - private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) + private static UserInfoPartial fillUserInfoWithTenantIds_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - return userInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, Arrays.asList(userInfo)).get(0); + return fillUserInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, Arrays.asList(userInfo)).get(0); } - private static List userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, List userInfos) + private static List fillUserInfoWithTenantIds_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userInfos) throws SQLException, StorageQueryException { String[] userIds = new String[userInfos.size()]; for (int i = 0; i < userInfos.size(); i++) { userIds[i] = userInfos.get(i).id; } - Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, appIdentifier, userIds); - List result = new ArrayList<>(); + Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, + appIdentifier, + userIds); + List result = new ArrayList<>(); for (UserInfoPartial userInfo : userInfos) { - result.add(new UserInfo(userInfo.id, userInfo.email, userInfo.passwordHash, userInfo.timeJoined, - tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]))); + userInfo.tenantIds = tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]); } - return result; + return userInfos; + } + + private static List fillUserInfoWithTenantIds(Start start, + AppIdentifier appIdentifier, + List userInfos) + throws SQLException, StorageQueryException { + String[] userIds = new String[userInfos.size()]; + for (int i = 0; i < userInfos.size(); i++) { + userIds[i] = userInfos.get(i).id; + } + + Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds(start, + appIdentifier, + userIds); + for (UserInfoPartial userInfo : userInfos) { + userInfo.tenantIds = tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]); + } + + return userInfos; } private static class UserInfoPartial { @@ -521,6 +687,9 @@ private static class UserInfoPartial { public final long timeJoined; public final String email; public final String passwordHash; + public String[] tenantIds; + public Boolean verified; + public Boolean isPrimary; public UserInfoPartial(String id, String email, String passwordHash, long timeJoined) { this.id = id.trim(); @@ -528,6 +697,13 @@ public UserInfoPartial(String id, String email, String passwordHash, long timeJo this.email = email; this.passwordHash = passwordHash; } + + public LoginMethod toLoginMethod() { + assert (tenantIds != null); + assert (verified != null); + return new LoginMethod(id, timeJoined, verified, email, + passwordHash, tenantIds); + } } private static class PasswordResetRowMapper implements RowMapper { @@ -544,7 +720,7 @@ private static PasswordResetRowMapper getInstance() { public PasswordResetTokenInfo map(ResultSet result) throws StorageQueryException { try { return new PasswordResetTokenInfo(result.getString("user_id"), result.getString("token"), - result.getLong("token_expiry")); + result.getLong("token_expiry"), result.getString("email")); } catch (Exception e) { throw new StorageQueryException(e); } 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 afe360fb..86b9359d 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java @@ -19,10 +19,8 @@ import io.supertokens.pluginInterface.RowMapper; import io.supertokens.pluginInterface.emailverification.EmailVerificationTokenInfo; 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.multitenancy.TenantIdentifierWithStorage; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; @@ -30,8 +28,7 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update; @@ -52,7 +49,7 @@ static String getQueryToCreateEmailVerificationTable(Start start) { + " PRIMARY KEY (app_id, user_id, email)," + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTable, "app_id", "fkey") + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -71,13 +68,14 @@ static String getQueryToCreateEmailVerificationTokensTable(Start start) { + "tenant_id VARCHAR(64) DEFAULT 'public'," + "user_id VARCHAR(128) NOT NULL," + "email VARCHAR(256) NOT NULL," - + "token VARCHAR(128) NOT NULL CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, "token", "key") + " UNIQUE," + + "token VARCHAR(128) NOT NULL CONSTRAINT " + + Utils.getConstraintName(schema, emailVerificationTokensTable, "token", "key") + " UNIQUE," + "token_expiry BIGINT NOT NULL," + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, null, "pkey") + " PRIMARY KEY (app_id, tenant_id, user_id, email, token), " + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, "tenant_id", "fkey") + " FOREIGN KEY(app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE" + + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE" + ")"; // @formatter:on } @@ -100,7 +98,8 @@ public static void deleteExpiredEmailVerificationTokens(Start start) throws SQLE public static void updateUsersIsEmailVerified_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String email, - boolean isEmailVerified) throws SQLException, StorageQueryException { + boolean isEmailVerified) + throws SQLException, StorageQueryException { if (isEmailVerified) { String QUERY = "INSERT INTO " + getConfig(start).getEmailVerificationTable() @@ -124,8 +123,10 @@ public static void updateUsersIsEmailVerified_Transaction(Start start, Connectio } public static void deleteAllEmailVerificationTokensForUser_Transaction(Start start, Connection con, - TenantIdentifier tenantIdentifier, String userId, - String email) throws SQLException, StorageQueryException { + TenantIdentifier tenantIdentifier, + String userId, + String email) + throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTokensTable() + " WHERE app_id = ? AND tenant_id = ? AND user_id = ? AND email = ?"; @@ -137,7 +138,8 @@ public static void deleteAllEmailVerificationTokensForUser_Transaction(Start sta }); } - public static EmailVerificationTokenInfo getEmailVerificationTokenInfo(Start start, TenantIdentifier tenantIdentifier, + public static EmailVerificationTokenInfo getEmailVerificationTokenInfo(Start start, + TenantIdentifier tenantIdentifier, String token) throws SQLException, StorageQueryException { String QUERY = "SELECT user_id, token, token_expiry, email FROM " @@ -155,7 +157,8 @@ public static EmailVerificationTokenInfo getEmailVerificationTokenInfo(Start sta }); } - public static void addEmailVerificationToken(Start start, TenantIdentifier tenantIdentifier, String userId, String tokenHash, long expiry, + public static void addEmailVerificationToken(Start start, TenantIdentifier tenantIdentifier, String userId, + String tokenHash, long expiry, String email) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getEmailVerificationTokensTable() + "(app_id, tenant_id, user_id, token, token_expiry, email)" + " VALUES(?, ?, ?, ?, ?, ?)"; @@ -173,10 +176,13 @@ public static void addEmailVerificationToken(Start start, TenantIdentifier tenan public static EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, - String userId, String email) throws SQLException, StorageQueryException { + String userId, + String email) + throws SQLException, StorageQueryException { String QUERY = "SELECT user_id, token, token_expiry, email FROM " - + getConfig(start).getEmailVerificationTokensTable() + " WHERE app_id = ? AND tenant_id = ? AND user_id = ? AND email = ? FOR UPDATE"; + + getConfig(start).getEmailVerificationTokensTable() + + " WHERE app_id = ? AND tenant_id = ? AND user_id = ? AND email = ? FOR UPDATE"; return execute(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -199,9 +205,11 @@ public static EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUs public static EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser(Start start, TenantIdentifier tenantIdentifier, String userId, - String email) throws SQLException, StorageQueryException { + String email) + throws SQLException, StorageQueryException { String QUERY = "SELECT user_id, token, token_expiry, email FROM " - + getConfig(start).getEmailVerificationTokensTable() + " WHERE app_id = ? AND tenant_id = ? AND user_id = ? AND email = ?"; + + getConfig(start).getEmailVerificationTokensTable() + + " WHERE app_id = ? AND tenant_id = ? AND user_id = ? AND email = ?"; return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -233,38 +241,130 @@ public static boolean isEmailVerified(Start start, AppIdentifier appIdentifier, }, result -> result.next()); } - public static void deleteUserInfo(Start start, AppIdentifier appIdentifier, String userId) - throws StorageQueryException, StorageTransactionLogicException { - start.startTransaction(con -> { - Connection sqlCon = (Connection) con.getConnection(); - try { - { - String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTable() - + " WHERE app_id = ? AND user_id = ?"; - update(sqlCon, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); - }); - } + public static class UserIdAndEmail { + public String userId; + public String email; - { - String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTokensTable() - + " WHERE app_id = ? AND user_id = ?"; + public UserIdAndEmail(String userId, String email) { + this.userId = userId; + this.email = email; + } + } + + // returns list of userIds where email is verified. + public static List isEmailVerified_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + List userIdAndEmail) + throws SQLException, StorageQueryException { + if (userIdAndEmail.isEmpty()) { + return new ArrayList<>(); + } + List emails = new ArrayList<>(); + List userIds = new ArrayList<>(); + Map userIdToEmailMap = new HashMap<>(); + for (UserIdAndEmail ue : userIdAndEmail) { + emails.add(ue.email); + userIds.add(ue.userId); + } + for (UserIdAndEmail ue : userIdAndEmail) { + if (userIdToEmailMap.containsKey(ue.userId)) { + throw new RuntimeException("Found a bug!"); + } + userIdToEmailMap.put(ue.userId, ue.email); + } + String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable() + + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")"; - update(sqlCon, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); - }); + return execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + int index = 2; + for (String userId : userIds) { + pst.setString(index++, userId); + } + for (String email : emails) { + pst.setString(index++, email); + } + }, result -> { + List res = new ArrayList<>(); + while (result.next()) { + String userId = result.getString("user_id"); + String email = result.getString("email"); + if (Objects.equals(userIdToEmailMap.get(userId), email)) { + res.add(userId); } + } + return res; + }); + } - sqlCon.commit(); - } catch (SQLException throwables) { - throw new StorageTransactionLogicException(throwables); + public static List isEmailVerified(Start start, AppIdentifier appIdentifier, + List userIdAndEmail) + throws SQLException, StorageQueryException { + if (userIdAndEmail.isEmpty()) { + return new ArrayList<>(); + } + List emails = new ArrayList<>(); + List userIds = new ArrayList<>(); + Map userIdToEmailMap = new HashMap<>(); + for (UserIdAndEmail ue : userIdAndEmail) { + emails.add(ue.email); + userIds.add(ue.userId); + } + for (UserIdAndEmail ue : userIdAndEmail) { + if (userIdToEmailMap.containsKey(ue.userId)) { + throw new RuntimeException("Found a bug!"); } - return null; + userIdToEmailMap.put(ue.userId, ue.email); + } + String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable() + + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + int index = 2; + for (String userId : userIds) { + pst.setString(index++, userId); + } + for (String email : emails) { + pst.setString(index++, email); + } + }, result -> { + List res = new ArrayList<>(); + while (result.next()) { + String userId = result.getString("user_id"); + String email = result.getString("email"); + if (Objects.equals(userIdToEmailMap.get(userId), email)) { + res.add(userId); + } + } + return res; }); } + public static void deleteUserInfo_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, + String userId) + throws StorageQueryException, SQLException { + { + String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } + + { + String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTokensTable() + + " WHERE app_id = ? AND user_id = ?"; + + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } + } + public static boolean deleteUserInfo(Start start, TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException, SQLException { String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTokensTable() 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 bc012896..81583518 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -20,6 +20,7 @@ import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.RowMapper; import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; +import io.supertokens.pluginInterface.authRecipe.LoginMethod; import io.supertokens.pluginInterface.dashboard.DashboardSearchTags; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; @@ -36,6 +37,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.stream.Collectors; import static io.supertokens.storage.postgresql.PreparedStatementValueSetter.NO_OP_SETTER; import static io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.CREATING_NEW_TABLE; @@ -72,13 +74,19 @@ static String getQueryToCreateUsersTable(Start start) { + "app_id VARCHAR(64) DEFAULT 'public'," + "tenant_id VARCHAR(64) DEFAULT 'public'," + "user_id CHAR(36) NOT NULL," + + "primary_or_recipe_user_id CHAR(36) NOT NULL," + + "is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE," + "recipe_id VARCHAR(128) NOT NULL," + "time_joined BIGINT NOT NULL," + + "primary_or_recipe_user_time_joined BIGINT NOT NULL," + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, null, "pkey") + " PRIMARY KEY (app_id, tenant_id, user_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "tenant_id", "fkey") + " FOREIGN KEY(app_id, tenant_id)" + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE," + + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "primary_or_recipe_user_id", "fkey") + + " FOREIGN KEY(app_id, primary_or_recipe_user_id)" + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE," + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "user_id", "fkey") + " FOREIGN KEY(app_id, user_id)" + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + @@ -97,9 +105,44 @@ public static String getQueryToCreateTenantIdIndexForUsersTable(Start start) { + Config.getConfig(start).getUsersTable() + "(app_id, tenant_id);"; } - static String getQueryToCreateUserPaginationIndex(Start start) { - return "CREATE INDEX all_auth_recipe_users_pagination_index ON " + Config.getConfig(start).getUsersTable() - + "(time_joined DESC, user_id DESC, tenant_id DESC, app_id DESC);"; + static String getQueryToCreateUserPaginationIndex1(Start start) { + return "CREATE INDEX all_auth_recipe_users_pagination_index1 ON " + Config.getConfig(start).getUsersTable() + + "(app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC);"; + } + + static String getQueryToCreateUserPaginationIndex2(Start start) { + return "CREATE INDEX all_auth_recipe_users_pagination_index2 ON " + Config.getConfig(start).getUsersTable() + + "(app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC);"; + } + + static String getQueryToCreateUserPaginationIndex3(Start start) { + return "CREATE INDEX all_auth_recipe_users_pagination_index3 ON " + Config.getConfig(start).getUsersTable() + + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC);"; + } + + static String getQueryToCreateUserPaginationIndex4(Start start) { + return "CREATE INDEX all_auth_recipe_users_pagination_index4 ON " + Config.getConfig(start).getUsersTable() + + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC);"; + } + + static String getQueryToCreatePrimaryUserId(Start start) { + /* + * Used in: + * - does user exist + * */ + return "CREATE INDEX all_auth_recipe_users_primary_user_id_index ON " + + Config.getConfig(start).getUsersTable() + + "(primary_or_recipe_user_id, app_id);"; + } + + static String getQueryToCreateRecipeIdIndex(Start start) { + /* + * Used in: + * - user count query + * */ + return "CREATE INDEX all_auth_recipe_users_recipe_id_index ON " + + Config.getConfig(start).getUsersTable() + + "(app_id, recipe_id, tenant_id);"; } private static String getQueryToCreateAppsTable(Start start) { @@ -109,8 +152,8 @@ private static String getQueryToCreateAppsTable(Start start) { return "CREATE TABLE IF NOT EXISTS " + appsTable + " (" + "app_id VARCHAR(64) NOT NULL DEFAULT 'public'," + "created_at_time BIGINT ," - + "CONSTRAINT " + Utils.getConstraintName(schema, appsTable, null, "pkey") - + " PRIMARY KEY(app_id)" + + + "CONSTRAINT " + Utils.getConstraintName(schema, appsTable, null, "pkey") + + " PRIMARY KEY(app_id)" + " );"; // @formatter:on } @@ -169,8 +212,13 @@ private static String getQueryToCreateAppIdToUserIdTable(Start start) { + "app_id VARCHAR(64) NOT NULL DEFAULT 'public'," + "user_id CHAR(36) NOT NULL," + "recipe_id VARCHAR(128) NOT NULL," + + "primary_or_recipe_user_id CHAR(36) NOT NULL," + + "is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE," + "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, null, "pkey") + " PRIMARY KEY (app_id, user_id), " + + "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, "primary_or_recipe_user_id", "fkey") + + " FOREIGN KEY(app_id, primary_or_recipe_user_id)" + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE," + "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, "app_id", "fkey") + " FOREIGN KEY(app_id) REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" @@ -223,7 +271,12 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto update(start, getQueryToCreateUsersTable(start), NO_OP_SETTER); // index - update(start, getQueryToCreateUserPaginationIndex(start), NO_OP_SETTER); + update(start, getQueryToCreateUserPaginationIndex1(start), NO_OP_SETTER); + update(start, getQueryToCreateUserPaginationIndex2(start), NO_OP_SETTER); + update(start, getQueryToCreateUserPaginationIndex3(start), NO_OP_SETTER); + update(start, getQueryToCreateUserPaginationIndex4(start), NO_OP_SETTER); + update(start, getQueryToCreatePrimaryUserId(start), NO_OP_SETTER); + update(start, getQueryToCreateRecipeIdIndex(start), NO_OP_SETTER); } if (!doesTableExists(start, Config.getConfig(start).getUserLastActiveTable())) { @@ -231,7 +284,8 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto update(start, ActiveUsersQueries.getQueryToCreateUserLastActiveTable(start), NO_OP_SETTER); // Index - update(start, ActiveUsersQueries.getQueryToCreateAppIdIndexForUserLastActiveTable(start), NO_OP_SETTER); + update(start, ActiveUsersQueries.getQueryToCreateAppIdIndexForUserLastActiveTable(start), + NO_OP_SETTER); } if (!doesTableExists(start, Config.getConfig(start).getAccessTokenSigningKeysTable())) { @@ -262,7 +316,8 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto NO_OP_SETTER); // index - update(start, MultitenancyQueries.getQueryToCreateTenantIdIndexForTenantThirdPartyProvidersTable(start), + update(start, + MultitenancyQueries.getQueryToCreateTenantIdIndexForTenantThirdPartyProvidersTable(start), NO_OP_SETTER); } @@ -272,7 +327,9 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto NO_OP_SETTER); // index - update(start, MultitenancyQueries.getQueryToCreateThirdPartyIdIndexForTenantThirdPartyProviderClientsTable(start), + update(start, + MultitenancyQueries.getQueryToCreateThirdPartyIdIndexForTenantThirdPartyProviderClientsTable( + start), NO_OP_SETTER); } @@ -391,7 +448,8 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto update(start, UserRolesQueries.getQueryToCreateRolePermissionsTable(start), NO_OP_SETTER); // index update(start, UserRolesQueries.getQueryToCreateRolePermissionsPermissionIndex(start), NO_OP_SETTER); - update(start, UserRolesQueries.getQueryToCreateRoleIndexForRolePermissionsTable(start), NO_OP_SETTER); + update(start, UserRolesQueries.getQueryToCreateRoleIndexForRolePermissionsTable(start), + NO_OP_SETTER); } if (!doesTableExists(start, Config.getConfig(start).getUserRolesTable())) { @@ -409,7 +467,9 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto update(start, UserIdMappingQueries.getQueryToCreateUserIdMappingTable(start), NO_OP_SETTER); // index - update(start, UserIdMappingQueries.getQueryToCreateSupertokensUserIdIndexForUserIdMappingTable(start), NO_OP_SETTER); + update(start, + UserIdMappingQueries.getQueryToCreateSupertokensUserIdIndexForUserIdMappingTable(start), + NO_OP_SETTER); } if (!doesTableExists(start, Config.getConfig(start).getDashboardUsersTable())) { @@ -417,7 +477,8 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto update(start, DashboardQueries.getQueryToCreateDashboardUsersTable(start), NO_OP_SETTER); // Index - update(start, DashboardQueries.getQueryToCreateAppIdIndexForDashboardUsersTable(start), NO_OP_SETTER); + update(start, DashboardQueries.getQueryToCreateAppIdIndexForDashboardUsersTable(start), + NO_OP_SETTER); } if (!doesTableExists(start, Config.getConfig(start).getDashboardSessionsTable())) { @@ -604,7 +665,9 @@ 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(*) as total FROM " + getConfig(start).getUsersTable()); + StringBuilder QUERY = new StringBuilder( + "SELECT COUNT(DISTINCT primary_or_recipe_user_id) AS total FROM " + + getConfig(start).getUsersTable()); QUERY.append(" WHERE app_id = ?"); if (includeRecipeIds != null && includeRecipeIds.length > 0) { QUERY.append(" AND recipe_id IN ("); @@ -636,7 +699,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(*) as total FROM " + getConfig(start).getUsersTable()); + StringBuilder QUERY = new StringBuilder( + "SELECT COUNT(DISTINCT primary_or_recipe_user_id) AS total FROM " + getConfig(start).getUsersTable()); QUERY.append(" WHERE app_id = ? AND tenant_id = ?"); if (includeRecipeIds != null && includeRecipeIds.length > 0) { QUERY.append(" AND recipe_id IN ("); @@ -669,7 +733,8 @@ public static long getUsersCount(Start start, TenantIdentifier tenantIdentifier, public static boolean doesUserIdExist(Start start, 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. String QUERY = "SELECT 1 FROM " + getConfig(start).getAppIdToUserIdTable() + " WHERE app_id = ? AND user_id = ?"; return execute(start, QUERY, pst -> { @@ -678,15 +743,33 @@ public static boolean doesUserIdExist(Start start, AppIdentifier appIdentifier, }, ResultSet::next); } - public static boolean doesUserIdExist(Start start, TenantIdentifier tenantIdentifier, 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. + String QUERY = "SELECT 1 FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? AND user_id = ?"; + return execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }, ResultSet::next); + } + public static boolean doesUserIdExist(Start start, TenantIdentifier tenantIdentifier, 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. String QUERY = "SELECT 1 FROM " + getConfig(start).getUsersTable() - + " WHERE app_id = ? AND tenant_id = ? AND user_id = ?"; + + " WHERE app_id = ? AND tenant_id = ? AND user_id = ? UNION SELECT 1 FROM " + + getConfig(start).getUsersTable() + + " WHERE app_id = ? AND tenant_id = ? AND primary_or_recipe_user_id = ?"; return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, userId); + pst.setString(4, tenantIdentifier.getAppId()); + pst.setString(5, tenantIdentifier.getTenantId()); + pst.setString(6, userId); }, ResultSet::next); } @@ -698,7 +781,7 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant throws SQLException, StorageQueryException { // This list will be used to keep track of the result's order from the db - List usersFromQuery; + List usersFromQuery; if (dashboardSearchTags != null) { ArrayList queryList = new ArrayList<>(); @@ -740,7 +823,9 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant String QUERY = "SELECT allAuthUsersTable.*" + " FROM " + getConfig(start).getUsersTable() + " AS allAuthUsersTable" + " JOIN " + getConfig(start).getThirdPartyUserToTenantTable() - + " AS thirdPartyToTenantTable ON allAuthUsersTable.app_id = thirdPartyToTenantTable.app_id AND" + + + " AS thirdPartyToTenantTable ON allAuthUsersTable.app_id = thirdPartyToTenantTable" + + ".app_id AND" + " allAuthUsersTable.tenant_id = thirdPartyToTenantTable.tenant_id AND" + " allAuthUsersTable.user_id = thirdPartyToTenantTable.user_id" + " JOIN " + getConfig(start).getThirdPartyUsersTable() @@ -870,22 +955,20 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant usersFromQuery = new ArrayList<>(); } else { - String finalQuery = "SELECT * FROM ( " + USER_SEARCH_TAG_CONDITION.toString() + " )" - + " AS finalResultTable ORDER BY time_joined " + timeJoinedOrder + ", 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)); } }, result -> { - List temp = new ArrayList<>(); + List temp = new ArrayList<>(); while (result.next()) { - temp.add(new UserInfoPaginationResultHolder(result.getString("user_id"), - result.getString("recipe_id"))); + temp.add(result.getString("primary_or_recipe_user_id")); } return temp; }); } - } } else { @@ -909,11 +992,11 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant recipeIdCondition = recipeIdCondition + " AND"; } String timeJoinedOrderSymbol = timeJoinedOrder.equals("ASC") ? ">" : "<"; - String QUERY = "SELECT user_id, recipe_id FROM " + getConfig(start).getUsersTable() + " WHERE " - + recipeIdCondition + " (time_joined " + timeJoinedOrderSymbol - + " ? OR (time_joined = ? AND user_id <= ?)) AND app_id = ? AND tenant_id = ?" - + " ORDER BY time_joined " + timeJoinedOrder - + ", user_id DESC LIMIT ?"; + 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 = ?" + + " ORDER BY primary_or_recipe_user_time_joined " + timeJoinedOrder + + ", primary_or_recipe_user_id DESC LIMIT ?"; usersFromQuery = execute(start, QUERY, pst -> { if (includeRecipeIds != null) { for (int i = 0; i < includeRecipeIds.length; i++) { @@ -929,21 +1012,20 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant pst.setString(baseIndex + 5, tenantIdentifier.getTenantId()); pst.setInt(baseIndex + 6, limit); }, result -> { - List temp = new ArrayList<>(); + List temp = new ArrayList<>(); while (result.next()) { - temp.add(new UserInfoPaginationResultHolder(result.getString("user_id"), - result.getString("recipe_id"))); + temp.add(result.getString("primary_or_recipe_user_id")); } return temp; }); } else { String recipeIdCondition = RECIPE_ID_CONDITION.toString(); - String QUERY = "SELECT user_id, recipe_id 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"; } - QUERY += " app_id = ? AND tenant_id = ? ORDER BY time_joined " + timeJoinedOrder - + ", user_id DESC LIMIT ?"; + QUERY += " 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 -> { if (includeRecipeIds != null) { for (int i = 0; i < includeRecipeIds.length; i++) { @@ -956,75 +1038,507 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant pst.setString(baseIndex + 2, tenantIdentifier.getTenantId()); pst.setInt(baseIndex + 3, limit); }, result -> { - List temp = new ArrayList<>(); + List temp = new ArrayList<>(); while (result.next()) { - temp.add(new UserInfoPaginationResultHolder(result.getString("user_id"), - result.getString("recipe_id"))); + temp.add(result.getString("primary_or_recipe_user_id")); } return temp; }); } } - // we create a map from recipe ID -> userId[] - Map> recipeIdToUserIdListMap = new HashMap<>(); - for (UserInfoPaginationResultHolder user : usersFromQuery) { - RECIPE_ID recipeId = RECIPE_ID.getEnumFromString(user.recipeId); - if (recipeId == null) { - throw new SQLException("Unrecognised recipe ID in database: " + user.recipeId); + AuthRecipeUserInfo[] finalResult = new AuthRecipeUserInfo[usersFromQuery.size()]; + + List users = getPrimaryUserInfoForUserIds(start, + tenantIdentifier.toAppIdentifier(), + usersFromQuery); + + // we fill in all the slots in finalResult based on their position in + // usersFromQuery + Map userIdToInfoMap = new HashMap<>(); + for (AuthRecipeUserInfo user : users) { + userIdToInfoMap.put(user.getSupertokensUserId(), user); + } + for (int i = 0; i < usersFromQuery.size(); i++) { + if (finalResult[i] == null) { + finalResult[i] = userIdToInfoMap.get(usersFromQuery.get(i)); + } + } + + return finalResult; + } + + public static void makePrimaryUser_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + String userId) + throws SQLException, StorageQueryException { + { + String QUERY = "UPDATE " + getConfig(start).getUsersTable() + + " SET is_linked_or_is_a_primary_user = true WHERE app_id = ? AND user_id = ?"; + + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } + { + String QUERY = "UPDATE " + getConfig(start).getAppIdToUserIdTable() + + " SET is_linked_or_is_a_primary_user = true WHERE app_id = ? AND user_id = ?"; + + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } + } + + public static void linkAccounts_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + String recipeUserId, String primaryUserId) + throws SQLException, StorageQueryException { + { + String 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 = ?"; + + update(sqlCon, QUERY, pst -> { + pst.setString(1, primaryUserId); + pst.setString(2, appIdentifier.getAppId()); + pst.setString(3, recipeUserId); + }); + } + + updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, appIdentifier, primaryUserId); + + { + String 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 = ?"; + + update(sqlCon, QUERY, pst -> { + pst.setString(1, primaryUserId); + pst.setString(2, appIdentifier.getAppId()); + pst.setString(3, recipeUserId); + }); + } + } + + public static void unlinkAccounts_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + String primaryUserId, String recipeUserId) + throws SQLException, StorageQueryException { + { + String QUERY = "UPDATE " + getConfig(start).getUsersTable() + + " SET is_linked_or_is_a_primary_user = false, primary_or_recipe_user_id = ?, " + + "primary_or_recipe_user_time_joined = time_joined WHERE app_id = ? AND " + + "user_id = ?"; + + update(sqlCon, QUERY, pst -> { + pst.setString(1, recipeUserId); + pst.setString(2, appIdentifier.getAppId()); + pst.setString(3, recipeUserId); + }); + } + + updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, appIdentifier, primaryUserId); + + { + String QUERY = "UPDATE " + getConfig(start).getAppIdToUserIdTable() + + " SET is_linked_or_is_a_primary_user = false, primary_or_recipe_user_id = ?" + + " WHERE app_id = ? AND user_id = ?"; + + update(sqlCon, QUERY, pst -> { + pst.setString(1, recipeUserId); + pst.setString(2, appIdentifier.getAppId()); + pst.setString(3, recipeUserId); + }); + } + } + + public static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String phoneNumber) + throws SQLException, StorageQueryException { + // we first lock on the table based on phoneNumber and tenant - this will ensure that any other + // query happening related to the account linking on this phone number / tenant will wait for this to finish, + // and vice versa. + + PasswordlessQueries.lockPhoneAndTenant_Transaction(start, sqlCon, appIdentifier, phoneNumber); + + // now that we have locks on all the relevant tables, we can read from them safely + List userIds = PasswordlessQueries.listUserIdsByPhoneNumber_Transaction(start, sqlCon, appIdentifier, + phoneNumber); + + 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)); + + return result.toArray(new AuthRecipeUserInfo[0]); + } + + public static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo(Start start, + AppIdentifier appIdentifier, + String thirdPartyId, + String thirdPartyUserId) + throws SQLException, StorageQueryException { + List userIds = ThirdPartyQueries.listUserIdsByThirdPartyInfo(start, appIdentifier, + thirdPartyId, thirdPartyUserId); + List result = getPrimaryUserInfoForUserIds(start, appIdentifier, userIds); + + // this is going to order them based on oldest that joined to newest that joined. + result.sort(Comparator.comparingLong(o -> o.timeJoined)); + + return result.toArray(new AuthRecipeUserInfo[0]); + } + + public static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String thirdPartyId, + String thirdPartyUserId) + throws SQLException, StorageQueryException { + // we first lock on the table based on thirdparty info and tenant - this will ensure that any other + // query happening related to the account linking on this third party info / tenant will wait for this to + // finish, + // and vice versa. + + ThirdPartyQueries.lockThirdPartyInfoAndTenant_Transaction(start, sqlCon, appIdentifier, thirdPartyId, + thirdPartyUserId); + + // 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); + + // this is going to order them based on oldest that joined to newest that joined. + result.sort(Comparator.comparingLong(o -> o.timeJoined)); + + return result.toArray(new AuthRecipeUserInfo[0]); + } + + public static AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String email) + throws SQLException, StorageQueryException { + // we first lock on the three tables based on email and tenant - this will ensure that any other + // query happening related to the account linking on this email / tenant will wait for this to finish, + // and vice versa. + + EmailPasswordQueries.lockEmail_Transaction(start, sqlCon, appIdentifier, email); + + ThirdPartyQueries.lockEmail_Transaction(start, sqlCon, appIdentifier, email); + + PasswordlessQueries.lockEmail_Transaction(start, sqlCon, appIdentifier, email); + + // now that we have locks on all the relevant tables, we can read from them safely + List userIds = new ArrayList<>(); + userIds.addAll(EmailPasswordQueries.getPrimaryUserIdsUsingEmail_Transaction(start, sqlCon, appIdentifier, + email)); + + userIds.addAll(PasswordlessQueries.getPrimaryUserIdsUsingEmail_Transaction(start, sqlCon, appIdentifier, + email)); + + userIds.addAll(ThirdPartyQueries.getPrimaryUserIdUsingEmail_Transaction(start, sqlCon, appIdentifier, email)); + + // remove duplicates from userIds + Set userIdsSet = new HashSet<>(userIds); + userIds = new ArrayList<>(userIdsSet); + + List result = getPrimaryUserInfoForUserIds(start, appIdentifier, + userIds); + + // this is going to order them based on oldest that joined to newest that joined. + result.sort(Comparator.comparingLong(o -> o.timeJoined)); + + return result.toArray(new AuthRecipeUserInfo[0]); + } + + public static AuthRecipeUserInfo[] listPrimaryUsersByEmail(Start start, TenantIdentifier tenantIdentifier, + String email) + throws StorageQueryException, SQLException { + List userIds = new ArrayList<>(); + String emailPasswordUserId = EmailPasswordQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier, + email); + if (emailPasswordUserId != null) { + userIds.add(emailPasswordUserId); + } + + String passwordlessUserId = PasswordlessQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier, + email); + if (passwordlessUserId != null) { + userIds.add(passwordlessUserId); + } + + userIds.addAll(ThirdPartyQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier, email)); + + // remove duplicates from userIds + Set userIdsSet = new HashSet<>(userIds); + userIds = new ArrayList<>(userIdsSet); + + List result = getPrimaryUserInfoForUserIds(start, tenantIdentifier.toAppIdentifier(), + userIds); + + // this is going to order them based on oldest that joined to newest that joined. + result.sort(Comparator.comparingLong(o -> o.timeJoined)); + + return result.toArray(new AuthRecipeUserInfo[0]); + } + + public static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber(Start start, + TenantIdentifier tenantIdentifier, + String phoneNumber) + throws StorageQueryException, SQLException { + List userIds = new ArrayList<>(); + + String passwordlessUserId = PasswordlessQueries.getPrimaryUserByPhoneNumber(start, tenantIdentifier, + phoneNumber); + if (passwordlessUserId != null) { + userIds.add(passwordlessUserId); + } + + List result = getPrimaryUserInfoForUserIds(start, tenantIdentifier.toAppIdentifier(), + userIds); + + // this is going to order them based on oldest that joined to newest that joined. + result.sort(Comparator.comparingLong(o -> o.timeJoined)); + + return result.toArray(new AuthRecipeUserInfo[0]); + } + + public static AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(Start start, + TenantIdentifier tenantIdentifier, + String thirdPartyId, + String thirdPartyUserId) + throws StorageQueryException, SQLException { + String userId = ThirdPartyQueries.getUserIdByThirdPartyInfo(start, tenantIdentifier, + thirdPartyId, thirdPartyUserId); + return getPrimaryUserInfoForUserId(start, tenantIdentifier.toAppIdentifier(), userId); + } + + public static String getPrimaryUserIdStrForUserId(Start start, AppIdentifier appIdentifier, String id) + throws SQLException, StorageQueryException { + String QUERY = "SELECT primary_or_recipe_user_id FROM " + getConfig(start).getUsersTable() + + " WHERE user_id = ? AND app_id = ?"; + return execute(start, QUERY, pst -> { + pst.setString(1, id); + pst.setString(2, appIdentifier.getAppId()); + }, result -> { + if (result.next()) { + return result.getString("primary_or_recipe_user_id"); + } + return null; + }); + } + + public static AuthRecipeUserInfo getPrimaryUserInfoForUserId(Start start, AppIdentifier appIdentifier, String id) + throws SQLException, StorageQueryException { + List ids = new ArrayList<>(); + ids.add(id); + List result = getPrimaryUserInfoForUserIds(start, appIdentifier, ids); + if (result.isEmpty()) { + return null; + } + return result.get(0); + } + + public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, String id) + throws SQLException, StorageQueryException { + List ids = new ArrayList<>(); + ids.add(id); + List result = getPrimaryUserInfoForUserIds_Transaction(start, con, appIdentifier, ids); + if (result.isEmpty()) { + return null; + } + return result.get(0); + } + + private static List getPrimaryUserInfoForUserIds(Start start, + AppIdentifier appIdentifier, + List userIds) + throws StorageQueryException, SQLException { + if (userIds.size() == 0) { + return new ArrayList<>(); + } + + // We check both user_id and primary_or_recipe_user_id because the input may have a recipe userId + // 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 au.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 + int index = 1; + for (int i = 0; i < userIds.size(); i++, index++) { + pst.setString(index, userIds.get(i)); } - List userIdList = recipeIdToUserIdListMap.get(recipeId); - if (userIdList == null) { - userIdList = new ArrayList<>(); + // IN primary_or_recipe_user_id + for (int i = 0; i < userIds.size(); i++, index++) { + pst.setString(index, userIds.get(i)); } - userIdList.add(user.userId); - recipeIdToUserIdListMap.put(recipeId, userIdList); + // for app_id + pst.setString(index, appIdentifier.getAppId()); + pst.setString(index + 1, appIdentifier.getAppId()); + }, result -> { + List parsedResult = new ArrayList<>(); + while (result.next()) { + parsedResult.add(new AllAuthRecipeUsersResultHolder(result.getString("user_id"), + result.getString("tenant_id"), + result.getString("primary_or_recipe_user_id"), + result.getBoolean("is_linked_or_is_a_primary_user"), + result.getString("recipe_id"), + result.getLong("time_joined"))); + } + return parsedResult; + }); + + // Now we form the userIds again, but based on the user_id in the result from above. + Set recipeUserIdsToFetch = new HashSet<>(); + for (AllAuthRecipeUsersResultHolder user : allAuthUsersResult) { + // this will remove duplicate entries wherein a user id is shared across several tenants. + recipeUserIdsToFetch.add(user.userId); } - AuthRecipeUserInfo[] finalResult = new AuthRecipeUserInfo[usersFromQuery.size()]; + List loginMethods = new ArrayList<>(); + loginMethods.addAll( + EmailPasswordQueries.getUsersInfoUsingIdList(start, recipeUserIdsToFetch, appIdentifier)); + loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList(start, recipeUserIdsToFetch, appIdentifier)); + loginMethods.addAll( + PasswordlessQueries.getUsersInfoUsingIdList(start, recipeUserIdsToFetch, appIdentifier)); + + Map recipeUserIdToLoginMethodMap = new HashMap<>(); + for (LoginMethod loginMethod : loginMethods) { + recipeUserIdToLoginMethodMap.put(loginMethod.getSupertokensUserId(), loginMethod); + } - // we give the userId[] for each recipe to fetch all those user's details - for (RECIPE_ID recipeId : recipeIdToUserIdListMap.keySet()) { - List users = getUserInfoForRecipeIdFromUserIds(start, - tenantIdentifier.toAppIdentifier(), recipeId, recipeIdToUserIdListMap.get(recipeId)); + Map userIdToAuthRecipeUserInfo = new HashMap<>(); - // we fill in all the slots in finalResult based on their position in - // usersFromQuery - Map userIdToInfoMap = new HashMap<>(); - for (AuthRecipeUserInfo user : users) { - userIdToInfoMap.put(user.id, user); + for (AllAuthRecipeUsersResultHolder authRecipeUsersResultHolder : allAuthUsersResult) { + String recipeUserId = authRecipeUsersResultHolder.userId; + LoginMethod loginMethod = recipeUserIdToLoginMethodMap.get(recipeUserId); + + if (loginMethod == null) { + // loginMethod will be null for primaryUserId for which the user has been deleted during unlink + continue; } - for (int i = 0; i < usersFromQuery.size(); i++) { - if (finalResult[i] == null) { - finalResult[i] = userIdToInfoMap.get(usersFromQuery.get(i).userId); - } + + String primaryUserId = authRecipeUsersResultHolder.primaryOrRecipeUserId; + AuthRecipeUserInfo curr = userIdToAuthRecipeUserInfo.get(primaryUserId); + if (curr == null) { + curr = AuthRecipeUserInfo.create(primaryUserId, authRecipeUsersResultHolder.isLinkedOrIsAPrimaryUser, + loginMethod); + } else { + curr.addLoginMethod(loginMethod); } + userIdToAuthRecipeUserInfo.put(primaryUserId, curr); } - return finalResult; + return userIdToAuthRecipeUserInfo.keySet().stream().map(userIdToAuthRecipeUserInfo::get) + .collect(Collectors.toList()); } - private static List getUserInfoForRecipeIdFromUserIds(Start start, - AppIdentifier appIdentifier, - RECIPE_ID recipeId, - List userIds) + private static List getPrimaryUserInfoForUserIds_Transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userIds) throws StorageQueryException, SQLException { - if (recipeId == RECIPE_ID.EMAIL_PASSWORD) { - return EmailPasswordQueries.getUsersInfoUsingIdList(start, appIdentifier, userIds); - } else if (recipeId == RECIPE_ID.THIRD_PARTY) { - return ThirdPartyQueries.getUsersInfoUsingIdList(start, appIdentifier, userIds); - } else if (recipeId == RECIPE_ID.PASSWORDLESS) { - return PasswordlessQueries.getUsersByIdList(start, appIdentifier, userIds); - } else { - throw new IllegalArgumentException("No implementation of get users for recipe: " + recipeId.toString()); + if (userIds.size() == 0) { + return new ArrayList<>(); + } + + // We check both user_id and primary_or_recipe_user_id because the input may have a recipe userId + // 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 au.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 + int index = 1; + for (int i = 0; i < userIds.size(); i++, index++) { + pst.setString(index, userIds.get(i)); + } + // IN primary_or_recipe_user_id + for (int i = 0; i < userIds.size(); i++, index++) { + pst.setString(index, userIds.get(i)); + } + // for app_id + pst.setString(index, appIdentifier.getAppId()); + pst.setString(index + 1, appIdentifier.getAppId()); + }, result -> { + List parsedResult = new ArrayList<>(); + while (result.next()) { + parsedResult.add(new AllAuthRecipeUsersResultHolder(result.getString("user_id"), + result.getString("tenant_id"), + result.getString("primary_or_recipe_user_id"), + result.getBoolean("is_linked_or_is_a_primary_user"), + result.getString("recipe_id"), + result.getLong("time_joined"))); + } + return parsedResult; + }); + + // Now we form the userIds again, but based on the user_id in the result from above. + Set recipeUserIdsToFetch = new HashSet<>(); + for (AllAuthRecipeUsersResultHolder user : allAuthUsersResult) { + // this will remove duplicate entries wherein a user id is shared across several tenants. + recipeUserIdsToFetch.add(user.userId); + } + + List loginMethods = new ArrayList<>(); + loginMethods.addAll( + 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)); + + Map recipeUserIdToLoginMethodMap = new HashMap<>(); + for (LoginMethod loginMethod : loginMethods) { + recipeUserIdToLoginMethodMap.put(loginMethod.getSupertokensUserId(), loginMethod); + } + + Map userIdToAuthRecipeUserInfo = new HashMap<>(); + + for (AllAuthRecipeUsersResultHolder authRecipeUsersResultHolder : allAuthUsersResult) { + String recipeUserId = authRecipeUsersResultHolder.userId; + LoginMethod loginMethod = recipeUserIdToLoginMethodMap.get(recipeUserId); + if (loginMethod == null) { + // loginMethod will be null for primaryUserId for which the user has been deleted during unlink + continue; + } + String primaryUserId = authRecipeUsersResultHolder.primaryOrRecipeUserId; + AuthRecipeUserInfo curr = userIdToAuthRecipeUserInfo.get(primaryUserId); + if (curr == null) { + curr = AuthRecipeUserInfo.create(primaryUserId, authRecipeUsersResultHolder.isLinkedOrIsAPrimaryUser, + loginMethod); + } else { + curr.addLoginMethod(loginMethod); + } + userIdToAuthRecipeUserInfo.put(primaryUserId, curr); } + + return userIdToAuthRecipeUserInfo.keySet().stream().map(userIdToAuthRecipeUserInfo::get) + .collect(Collectors.toList()); } - public static String getRecipeIdForUser_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) + public static String getRecipeIdForUser_Transaction(Start start, Connection sqlCon, + TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { + String QUERY = "SELECT recipe_id FROM " + getConfig(start).getAppIdToUserIdTable() + " WHERE app_id = ? AND user_id = ? FOR UPDATE"; - return execute(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, userId); @@ -1036,12 +1550,14 @@ public static String getRecipeIdForUser_Transaction(Start start, Connection sqlC }); } - public static Map> getTenantIdsForUserIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String[] userIds) + public static Map> getTenantIdsForUserIds_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + String[] userIds) throws SQLException, StorageQueryException { if (userIds != null && userIds.length > 0) { StringBuilder QUERY = new StringBuilder("SELECT user_id, tenant_id " + "FROM " + getConfig(start).getUsersTable()); - QUERY.append(" WHERE app_id = ? AND user_id IN ("); + QUERY.append(" WHERE user_id IN ("); for (int i = 0; i < userIds.length; i++) { QUERY.append("?"); @@ -1050,14 +1566,57 @@ public static Map> getTenantIdsForUserIds_transaction(Start QUERY.append(","); } } - QUERY.append(")"); + QUERY.append(") AND app_id = ?"); return execute(sqlCon, QUERY.toString(), pst -> { - pst.setString(1, appIdentifier.getAppId()); for (int i = 0; i < userIds.length; i++) { - // i+2 cause this starts with 1 and not 0, and 1 is appId - pst.setString(i + 2, userIds[i]); + // i+1 cause this starts with 1 and not 0, and 1 is appId + pst.setString(i + 1, userIds[i]); + } + pst.setString(userIds.length + 1, appIdentifier.getAppId()); + }, result -> { + Map> finalResult = new HashMap<>(); + for (String userId : userIds) { + finalResult.put(userId, new ArrayList<>()); + } + + while (result.next()) { + String userId = result.getString("user_id").trim(); + String tenantId = result.getString("tenant_id"); + + finalResult.get(userId).add(tenantId); + } + return finalResult; + }); + } + + return new HashMap<>(); + } + + public static Map> getTenantIdsForUserIds(Start start, + AppIdentifier appIdentifier, + String[] userIds) + throws SQLException, StorageQueryException { + if (userIds != null && userIds.length > 0) { + StringBuilder QUERY = new StringBuilder("SELECT user_id, tenant_id " + + "FROM " + getConfig(start).getUsersTable()); + QUERY.append(" WHERE user_id IN ("); + for (int i = 0; i < userIds.length; i++) { + + QUERY.append("?"); + if (i != userIds.length - 1) { + // not the last element + QUERY.append(","); } + } + QUERY.append(") AND app_id = ?"); + + return execute(start, QUERY.toString(), pst -> { + for (int i = 0; i < userIds.length; i++) { + // i+1 cause this starts with 1 and not 0, and 1 is appId + pst.setString(i + 1, userIds[i]); + } + pst.setString(userIds.length + 1, appIdentifier.getAppId()); }, result -> { Map> finalResult = new HashMap<>(); for (String userId : userIds) { @@ -1107,12 +1666,13 @@ public static String[] getAllTablesInTheDatabaseThatHasDataForAppId(Start start, List result = new ArrayList<>(); for (String tableName : tableNames) { - String QUERY = "SELECT 1 FROM " + Config.getConfig(start).getTableSchema() + "." + tableName + " WHERE app_id = ?"; + String QUERY = + "SELECT 1 FROM " + Config.getConfig(start).getTableSchema() + "." + tableName + " WHERE app_id = ?"; boolean hasRows = execute(start, QUERY, pst -> { pst.setString(1, appId); }, res -> { - return res.next(); + return res.next(); }); if (hasRows) { result.add(tableName); @@ -1122,13 +1682,87 @@ public static String[] getAllTablesInTheDatabaseThatHasDataForAppId(Start start, return result.toArray(new String[0]); } - private static class UserInfoPaginationResultHolder { - String userId; - String recipeId; + public static int getUsersCountWithMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String QUERY = "SELECT COUNT (1) as c FROM (" + + " SELECT COUNT(user_id) as num_login_methods " + + " FROM " + getConfig(start).getUsersTable() + + " WHERE app_id = ? " + + " GROUP BY (app_id, primary_or_recipe_user_id) " + + ") as nloginmethods WHERE num_login_methods > 1"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + }, result -> { + return result.next() ? result.getInt("c") : 0; + }); + } + + public static boolean checkIfUsesAccountLinking(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + String QUERY = "SELECT 1 FROM " + + getConfig(start).getUsersTable() + + " WHERE app_id = ? AND is_linked_or_is_a_primary_user = true LIMIT 1"; + + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + }, result -> { + return result.next(); + }); + } + + public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) + throws SQLException, StorageQueryException { + GeneralQueries.AccountLinkingInfo accountLinkingInfo = new GeneralQueries.AccountLinkingInfo(userId, false); + { + String QUERY = "SELECT primary_or_recipe_user_id, is_linked_or_is_a_primary_user FROM " + + Config.getConfig(start).getAppIdToUserIdTable() + " WHERE app_id = ? AND user_id = ?"; + + accountLinkingInfo = execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }, result -> { + if (result.next()) { + String primaryUserId1 = result.getString("primary_or_recipe_user_id"); + boolean isLinked1 = result.getBoolean("is_linked_or_is_a_primary_user"); + return new AccountLinkingInfo(primaryUserId1, isLinked1); + } + return null; + }); + } + return accountLinkingInfo; + } + + 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 " + + getConfig(start).getUsersTable() + " WHERE app_id = ? AND primary_or_recipe_user_id = ?) WHERE " + + " app_id = ? AND primary_or_recipe_user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, primaryUserId); + pst.setString(3, appIdentifier.getAppId()); + pst.setString(4, primaryUserId); + }); + } - UserInfoPaginationResultHolder(String userId, String recipeId) { - this.userId = userId; - this.recipeId = recipeId; + private static class AllAuthRecipeUsersResultHolder { + String userId; + String tenantId; + String primaryOrRecipeUserId; + boolean isLinkedOrIsAPrimaryUser; + RECIPE_ID recipeId; + long timeJoined; + + AllAuthRecipeUsersResultHolder(String userId, String tenantId, String primaryOrRecipeUserId, + boolean isLinkedOrIsAPrimaryUser, String recipeId, long timeJoined) { + this.userId = userId.trim(); + this.tenantId = tenantId; + this.primaryOrRecipeUserId = primaryOrRecipeUserId; + this.isLinkedOrIsAPrimaryUser = isLinkedOrIsAPrimaryUser; + this.recipeId = RECIPE_ID.getEnumFromString(recipeId); + this.timeJoined = timeJoined; } } @@ -1147,4 +1781,14 @@ public KeyValueInfo map(ResultSet result) throws Exception { return new KeyValueInfo(result.getString("value"), result.getLong("created_at_time")); } } + + public static class AccountLinkingInfo { + public String primaryUserId; + public boolean isLinked; + + public AccountLinkingInfo(String primaryUserId, boolean isLinked) { + this.primaryUserId = primaryUserId; + this.isLinked = isLinked; + } + } } 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 067e5afe..31858944 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java @@ -17,13 +17,15 @@ package io.supertokens.storage.postgresql.queries; import io.supertokens.pluginInterface.RowMapper; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; +import io.supertokens.pluginInterface.authRecipe.LoginMethod; +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.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; -import io.supertokens.pluginInterface.passwordless.UserInfo; import io.supertokens.pluginInterface.sqlStorage.SQLStorage.TransactionIsolationLevel; import io.supertokens.storage.postgresql.ConnectionPool; import io.supertokens.storage.postgresql.Start; @@ -36,6 +38,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.stream.Collectors; import static io.supertokens.pluginInterface.RECIPE_ID.PASSWORDLESS; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; @@ -55,7 +58,8 @@ public static String getQueryToCreateUsersTable(Start start) { + "time_joined BIGINT NOT NULL, " + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "user_id", "fkey") + " FOREIGN KEY(app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE," + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + + " (app_id, user_id) ON DELETE CASCADE," + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, null, "pkey") + " PRIMARY KEY (app_id, user_id)" + ");"; @@ -79,7 +83,8 @@ static String getQueryToCreatePasswordlessUserToTenantTable(Start start) { + " PRIMARY KEY (app_id, tenant_id, user_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, "user_id", "fkey") + " FOREIGN KEY (app_id, tenant_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getUsersTable() + "(app_id, tenant_id, user_id) ON DELETE CASCADE" + + " REFERENCES " + Config.getConfig(start).getUsersTable() + + "(app_id, tenant_id, user_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -98,7 +103,7 @@ public static String getQueryToCreateDevicesTable(Start start) { + "failed_attempts INT NOT NULL," + "CONSTRAINT " + Utils.getConstraintName(schema, devicesTable, "tenant_id", "fkey") + " FOREIGN KEY(app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE," + + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE," + "CONSTRAINT " + Utils.getConstraintName(schema, devicesTable, null, "pkey") + " PRIMARY KEY (app_id, tenant_id, device_id_hash)" + ");"; @@ -126,7 +131,8 @@ public static String getQueryToCreateCodesTable(Start start) { + " PRIMARY KEY (app_id, tenant_id, code_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, codesTable, "device_id_hash", "fkey") + " FOREIGN KEY (app_id, tenant_id, device_id_hash)" - + " REFERENCES " + Config.getConfig(start).getPasswordlessDevicesTable() + "(app_id, tenant_id, device_id_hash)" + + " REFERENCES " + Config.getConfig(start).getPasswordlessDevicesTable() + + "(app_id, tenant_id, device_id_hash)" + " ON DELETE CASCADE ON UPDATE CASCADE" + ");"; } @@ -138,7 +144,8 @@ public static String getQueryToCreateDeviceEmailIndex(Start start) { public static String getQueryToCreateDevicePhoneNumberIndex(Start start) { return "CREATE INDEX passwordless_devices_phone_number_index ON " - + Config.getConfig(start).getPasswordlessDevicesTable() + " (app_id, tenant_id, phone_number);"; // USING hash + + Config.getConfig(start).getPasswordlessDevicesTable() + + " (app_id, tenant_id, phone_number);"; // USING hash } public static String getQueryToCreateCodeDeviceIdHashIndex(Start start) { @@ -151,8 +158,10 @@ public static String getQueryToCreateCodeCreatedAtIndex(Start start) { + Config.getConfig(start).getPasswordlessCodesTable() + "(app_id, tenant_id, created_at);"; } - public static void createDeviceWithCode(Start start, TenantIdentifier tenantIdentifier, String email, String phoneNumber, String linkCodeSalt, - PasswordlessCode code) throws StorageTransactionLogicException, StorageQueryException { + public static void createDeviceWithCode(Start start, TenantIdentifier tenantIdentifier, String email, + String phoneNumber, String linkCodeSalt, + PasswordlessCode code) + throws StorageTransactionLogicException, StorageQueryException { start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); try { @@ -197,7 +206,8 @@ public static PasswordlessDevice getDevice_Transaction(Start start, Connection c } public static void incrementDeviceFailedAttemptCount_Transaction(Start start, Connection con, - TenantIdentifier tenantIdentifier, String deviceIdHash) + TenantIdentifier tenantIdentifier, + String deviceIdHash) throws SQLException, StorageQueryException { String QUERY = "UPDATE " + getConfig(start).getPasswordlessDevicesTable() + " SET failed_attempts = failed_attempts + 1" @@ -210,7 +220,8 @@ public static void incrementDeviceFailedAttemptCount_Transaction(Start start, Co }); } - public static void deleteDevice_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String deviceIdHash) + public static void deleteDevice_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, + String deviceIdHash) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getPasswordlessDevicesTable() + " WHERE app_id = ? AND tenant_id = ? AND device_id_hash = ?"; @@ -221,7 +232,9 @@ public static void deleteDevice_Transaction(Start start, Connection con, TenantI }); } - public static void deleteDevicesByPhoneNumber_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, @Nonnull String phoneNumber) + public static void deleteDevicesByPhoneNumber_Transaction(Start start, Connection con, + TenantIdentifier tenantIdentifier, + @Nonnull String phoneNumber) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getPasswordlessDevicesTable() @@ -234,7 +247,8 @@ public static void deleteDevicesByPhoneNumber_Transaction(Start start, Connectio }); } - public static void deleteDevicesByPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, @Nonnull String phoneNumber, String userId) + public static void deleteDevicesByPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + @Nonnull String phoneNumber, String userId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getPasswordlessDevicesTable() @@ -251,7 +265,8 @@ public static void deleteDevicesByPhoneNumber_Transaction(Start start, Connectio }); } - public static void deleteDevicesByEmail_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, @Nonnull String email) + public static void deleteDevicesByEmail_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, + @Nonnull String email) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getPasswordlessDevicesTable() @@ -264,7 +279,8 @@ public static void deleteDevicesByEmail_Transaction(Start start, Connection con, }); } - public static void deleteDevicesByEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, @Nonnull String email, String userId) + public static void deleteDevicesByEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + @Nonnull String email, String userId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getPasswordlessDevicesTable() @@ -281,7 +297,8 @@ public static void deleteDevicesByEmail_Transaction(Start start, Connection con, }); } - private static void createCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, PasswordlessCode code) + private static void createCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, + PasswordlessCode code) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getPasswordlessCodesTable() + "(app_id, tenant_id, code_id, device_id_hash, link_code_hash, created_at)" @@ -311,7 +328,9 @@ public static void createCode(Start start, TenantIdentifier tenantIdentifier, Pa }); } - public static PasswordlessCode[] getCodesOfDevice_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String deviceIdHash) + public static PasswordlessCode[] getCodesOfDevice_Transaction(Start start, Connection con, + TenantIdentifier tenantIdentifier, + String deviceIdHash) throws StorageQueryException, SQLException { // We do not lock here, since the device is already locked earlier in the transaction. String QUERY = "SELECT code_id, device_id_hash, link_code_hash, created_at FROM " @@ -335,7 +354,9 @@ public static PasswordlessCode[] getCodesOfDevice_Transaction(Start start, Conne }); } - public static PasswordlessCode getCodeByLinkCodeHash_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String linkCodeHash) + public static PasswordlessCode getCodeByLinkCodeHash_Transaction(Start start, Connection con, + TenantIdentifier tenantIdentifier, + String linkCodeHash) throws StorageQueryException, SQLException { // We do not lock here, since the device is already locked earlier in the transaction. String QUERY = "SELECT code_id, device_id_hash, link_code_hash, created_at FROM " @@ -354,7 +375,8 @@ public static PasswordlessCode getCodeByLinkCodeHash_Transaction(Start start, Co }); } - public static void deleteCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String codeId) + public static void deleteCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, + String codeId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getPasswordlessCodesTable() + " WHERE app_id = ? AND tenant_id = ? AND code_id = ?"; @@ -366,30 +388,35 @@ public static void deleteCode_Transaction(Start start, Connection con, TenantIde }); } - public static UserInfo createUser(Start start, TenantIdentifier tenantIdentifier, String id, @Nullable String email, @Nullable String phoneNumber, long timeJoined) + 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 -> { Connection sqlCon = (Connection) con.getConnection(); try { { // app_id_to_user_id String QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() - + "(app_id, user_id, recipe_id)" + " VALUES(?, ?, ?)"; + + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, id); - pst.setString(3, PASSWORDLESS.toString()); + pst.setString(3, id); + pst.setString(4, PASSWORDLESS.toString()); }); } { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, recipe_id, time_joined)" + " VALUES(?, ?, ?, ?, ?)"; + + "(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()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, id); - pst.setString(4, PASSWORDLESS.toString()); - pst.setLong(5, timeJoined); + pst.setString(4, id); + pst.setString(5, PASSWORDLESS.toString()); + pst.setLong(6, timeJoined); + pst.setLong(7, timeJoined); }); } @@ -417,16 +444,20 @@ public static UserInfo createUser(Start start, TenantIdentifier tenantIdentifier pst.setString(5, phoneNumber); }); } - UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), new UserInfoPartial(id, email, phoneNumber, timeJoined)); + UserInfoPartial userInfo = new UserInfoPartial(id, email, phoneNumber, timeJoined); + fillUserInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); + fillUserInfoWithVerified_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); sqlCon.commit(); - return userInfo; + return AuthRecipeUserInfo.create(id, false, + userInfo.toLoginMethod()); } catch (SQLException throwables) { throw new StorageTransactionLogicException(throwables); } }); } - private static UserInfoWithTenantId[] getUserInfosWithTenant(Start start, Connection con, AppIdentifier appIdentifier, String userId) + private static UserInfoWithTenantId[] getUserInfosWithTenant_Transaction(Start start, Connection con, + 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 " @@ -453,49 +484,59 @@ private static UserInfoWithTenantId[] getUserInfosWithTenant(Start start, Connec }); } - public static void deleteUser(Start start, AppIdentifier appIdentifier, String userId) - throws StorageQueryException, StorageTransactionLogicException { - start.startTransaction(con -> { - Connection sqlCon = (Connection) con.getConnection(); - try { - UserInfoWithTenantId[] userInfos = getUserInfosWithTenant(start, sqlCon, appIdentifier, userId); + public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, + String userId, boolean deleteUserIdMappingToo) + throws StorageQueryException, SQLException { + UserInfoWithTenantId[] userInfos = getUserInfosWithTenant_Transaction(start, sqlCon, appIdentifier, userId); - { - String QUERY = "DELETE FROM " + getConfig(start).getAppIdToUserIdTable() - + " WHERE app_id = ? AND user_id = ?"; + if (deleteUserIdMappingToo) { + String QUERY = "DELETE FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? AND user_id = ?"; - update(sqlCon, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); - }); - } + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } else { + { + String QUERY = "DELETE FROM " + getConfig(start).getUsersTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } - for (UserInfoWithTenantId userInfo : userInfos) { - if (userInfo.email != null) { - deleteDevicesByEmail_Transaction(start, sqlCon, - new TenantIdentifier( - appIdentifier.getConnectionUriDomain(), appIdentifier.getAppId(), - userInfo.tenantId), - userInfo.email); - } - if (userInfo.phoneNumber != null) { - deleteDevicesByPhoneNumber_Transaction(start, sqlCon, - new TenantIdentifier( - appIdentifier.getConnectionUriDomain(), appIdentifier.getAppId(), - userInfo.tenantId), - userInfo.phoneNumber); - } - } + { + String QUERY = "DELETE FROM " + getConfig(start).getPasswordlessUsersTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } + } - sqlCon.commit(); - } catch (SQLException throwables) { - throw new StorageTransactionLogicException(throwables); + for (UserInfoWithTenantId userInfo : userInfos) { + if (userInfo.email != null) { + deleteDevicesByEmail_Transaction(start, sqlCon, + new TenantIdentifier( + appIdentifier.getConnectionUriDomain(), appIdentifier.getAppId(), + userInfo.tenantId), + userInfo.email); } - return null; - }); + if (userInfo.phoneNumber != null) { + deleteDevicesByPhoneNumber_Transaction(start, sqlCon, + new TenantIdentifier( + appIdentifier.getConnectionUriDomain(), appIdentifier.getAppId(), + userInfo.tenantId), + userInfo.phoneNumber); + } + } } - public static int updateUserEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String email) + public static int updateUserEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String userId, String email) throws SQLException, StorageQueryException { { String QUERY = "UPDATE " + Config.getConfig(start).getPasswordlessUserToTenantTable() @@ -519,7 +560,8 @@ public static int updateUserEmail_Transaction(Start start, Connection con, AppId } } - public static int updateUserPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String phoneNumber) + public static int updateUserPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String userId, String phoneNumber) throws SQLException, StorageQueryException { { String QUERY = "UPDATE " + Config.getConfig(start).getPasswordlessUserToTenantTable() @@ -562,7 +604,8 @@ public static PasswordlessDevice getDevice(Start start, TenantIdentifier tenantI } } - public static PasswordlessDevice[] getDevicesByEmail(Start start, TenantIdentifier tenantIdentifier, @Nonnull String email) + public static PasswordlessDevice[] getDevicesByEmail(Start start, TenantIdentifier tenantIdentifier, + @Nonnull String email) throws StorageQueryException, SQLException { String QUERY = "SELECT device_id_hash, email, phone_number, link_code_salt, failed_attempts FROM " + getConfig(start).getPasswordlessDevicesTable() @@ -609,7 +652,8 @@ public static PasswordlessDevice[] getDevicesByPhoneNumber(Start start, TenantId }); } - public static PasswordlessCode[] getCodesOfDevice(Start start, TenantIdentifier tenantIdentifier, String deviceIdHash) + public static PasswordlessCode[] getCodesOfDevice(Start start, TenantIdentifier tenantIdentifier, + String deviceIdHash) throws StorageQueryException, SQLException { try (Connection con = ConnectionPool.getConnection(start)) { // We can call the transaction version here because it doesn't lock anything. @@ -617,7 +661,8 @@ public static PasswordlessCode[] getCodesOfDevice(Start start, TenantIdentifier } } - public static PasswordlessCode[] getCodesBefore(Start start, TenantIdentifier tenantIdentifier, long time) throws StorageQueryException, SQLException { + public static PasswordlessCode[] getCodesBefore(Start start, TenantIdentifier tenantIdentifier, long time) + throws StorageQueryException, SQLException { String QUERY = "SELECT code_id, device_id_hash, link_code_hash, created_at FROM " + getConfig(start).getPasswordlessCodesTable() + " WHERE app_id = ? AND tenant_id = ? AND created_at < ?"; @@ -639,7 +684,8 @@ public static PasswordlessCode[] getCodesBefore(Start start, TenantIdentifier te }); } - public static PasswordlessCode getCode(Start start, TenantIdentifier tenantIdentifier, String codeId) throws StorageQueryException, SQLException { + public static PasswordlessCode getCode(Start start, TenantIdentifier tenantIdentifier, String codeId) + throws StorageQueryException, SQLException { String QUERY = "SELECT code_id, device_id_hash, link_code_hash, created_at FROM " + getConfig(start).getPasswordlessCodesTable() + " WHERE app_id = ? AND tenant_id = ? AND code_id = ?"; @@ -656,7 +702,8 @@ public static PasswordlessCode getCode(Start start, TenantIdentifier tenantIdent }); } - public static PasswordlessCode getCodeByLinkCodeHash(Start start, TenantIdentifier tenantIdentifier, String linkCodeHash) + public static PasswordlessCode getCodeByLinkCodeHash(Start start, TenantIdentifier tenantIdentifier, + String linkCodeHash) throws StorageQueryException, SQLException { try (Connection con = ConnectionPool.getConnection(start)) { // We can call the transaction version here because it doesn't lock anything. @@ -664,28 +711,52 @@ public static PasswordlessCode getCodeByLinkCodeHash(Start start, TenantIdentifi } } - public static List getUsersByIdList(Start start, AppIdentifier appIdentifier, List ids) + public static List getUsersInfoUsingIdList(Start start, Set ids, + 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 - StringBuilder QUERY = new StringBuilder("SELECT user_id, email, phone_number, time_joined " - + "FROM " + getConfig(start).getPasswordlessUsersTable()); - QUERY.append(" WHERE app_id = ? AND user_id IN ("); - for (int i = 0; i < ids.size(); i++) { - QUERY.append("?"); - if (i != ids.size() - 1) { - // not the last element - QUERY.append(","); + String QUERY = "SELECT user_id, email, phone_number, time_joined " + + "FROM " + getConfig(start).getPasswordlessUsersTable() + " WHERE user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(ids.size()) + ") AND app_id = ?"; + + List userInfos = execute(start, QUERY, pst -> { + int index = 1; + for (String id : ids) { + pst.setString(index, id); + index++; } - } - QUERY.append(")"); + pst.setString(index, appIdentifier.getAppId()); + }, result -> { + List finalResult = new ArrayList<>(); + while (result.next()) { + finalResult.add(UserInfoRowMapper.getInstance().mapOrThrow(result)); + } + return finalResult; + }); + fillUserInfoWithTenantIds(start, appIdentifier, userInfos); + fillUserInfoWithVerified(start, appIdentifier, userInfos); + return userInfos.stream().map(UserInfoPartial::toLoginMethod).collect(Collectors.toList()); + } + return Collections.emptyList(); + } - List userInfos = execute(start, QUERY.toString(), pst -> { - pst.setString(1, appIdentifier.getAppId()); - for (int i = 0; i < ids.size(); i++) { - // i+2 cause this starts with 1 and not 0, 1 is appId - pst.setString(i + 2, ids.get(i)); + public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, + 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 + String QUERY = "SELECT user_id, email, phone_number, time_joined " + + "FROM " + getConfig(start).getPasswordlessUsersTable() + " WHERE user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(ids.size()) + ") AND app_id = ?"; + + List userInfos = execute(con, QUERY, pst -> { + int index = 1; + for (String id : ids) { + pst.setString(index, id); + index++; } + pst.setString(index, appIdentifier.getAppId()); }, result -> { List finalResult = new ArrayList<>(); while (result.next()) { @@ -693,16 +764,22 @@ public static List getUsersByIdList(Start start, AppIdentifier appIden } return finalResult; }); - return userInfoWithTenantIds(start, appIdentifier, userInfos); + fillUserInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); + fillUserInfoWithVerified_transaction(start, con, appIdentifier, userInfos); + return userInfos.stream().map(UserInfoPartial::toLoginMethod).collect(Collectors.toList()); } return Collections.emptyList(); } - public static UserInfo getUserById(Start start, AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { + private static UserInfoPartial getUserById_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, + 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 String QUERY = "SELECT user_id, email, phone_number, time_joined FROM " + getConfig(start).getPasswordlessUsersTable() + " WHERE app_id = ? AND user_id = ?"; - UserInfoPartial userInfo = execute(start, QUERY, pst -> { + return execute(sqlCon, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); pst.setString(2, userId); }, result -> { @@ -711,92 +788,166 @@ public static UserInfo getUserById(Start start, AppIdentifier appIdentifier, Str } return null; }); - return userInfoWithTenantIds(start, appIdentifier, userInfo); } - public static UserInfoPartial getUserById(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { - // 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, phone_number, time_joined FROM " - + getConfig(start).getPasswordlessUsersTable() - + " WHERE app_id = ? AND user_id = ?"; + public static List lockEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String email) throws StorageQueryException, SQLException { + // 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 FROM " + getConfig(start).getPasswordlessUsersTable() + + " WHERE app_id = ? AND email = ? FOR UPDATE"; - return execute(sqlCon, QUERY, pst -> { + return execute(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); + pst.setString(2, email); }, result -> { - if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); } - return null; + return userIds; + }); + } + + public static List lockPhoneAndTenant_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String phoneNumber) + throws SQLException, StorageQueryException { + + String QUERY = "SELECT user_id FROM " + getConfig(start).getPasswordlessUsersTable() + + " WHERE app_id = ? AND phone_number = ? FOR UPDATE"; + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, phoneNumber); + }, result -> { + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); + } + return userIds; }); } - public static UserInfo getUserByEmail(Start start, TenantIdentifier tenantIdentifier, @Nonnull String email) + public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier, + String email) 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.time_joined as time_joined " - + "FROM " + getConfig(start).getPasswordlessUserToTenantTable() + " AS pl_users_to_tenant " - + "JOIN " + getConfig(start).getPasswordlessUsersTable() + " AS pl_users " - + "ON pl_users.app_id = pl_users_to_tenant.app_id AND pl_users.user_id = pl_users_to_tenant.user_id " - + "WHERE pl_users_to_tenant.app_id = ? AND pl_users_to_tenant.tenant_id = ? AND pl_users_to_tenant.email = ? "; + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getPasswordlessUserToTenantTable() + " 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.tenant_id = ? AND pless.email = ?"; - UserInfoPartial userInfo = execute(start, QUERY, pst -> { + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, email); }, result -> { if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); + return result.getString("user_id"); } return null; }); - return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); } - public static UserInfo getUserByPhoneNumber(Start start, TenantIdentifier tenantIdentifier, @Nonnull String phoneNumber) + public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String email) 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.time_joined as time_joined " - + "FROM " + getConfig(start).getPasswordlessUserToTenantTable() + " AS pl_users_to_tenant " - + "JOIN " + getConfig(start).getPasswordlessUsersTable() + " AS pl_users " - + "ON pl_users.app_id = pl_users_to_tenant.app_id AND pl_users.user_id = pl_users_to_tenant.user_id " - + "WHERE pl_users_to_tenant.app_id = ? AND pl_users_to_tenant.tenant_id = ? AND pl_users_to_tenant.phone_number = ? "; + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" + + " JOIN " + getConfig(start).getAppIdToUserIdTable() + " 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.email = ?"; - UserInfoPartial userInfo = execute(start, QUERY, pst -> { + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, email); + }, 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 { + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getPasswordlessUserToTenantTable() + " 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.tenant_id = ? AND pless.phone_number = ?"; + + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, phoneNumber); }, result -> { if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); + return result.getString("user_id"); } return null; }); - return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); } - public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) + public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + @Nonnull String phoneNumber) throws StorageQueryException, SQLException { - UserInfoPartial userInfo = PasswordlessQueries.getUserById(start, sqlCon, + 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 = ?"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, phoneNumber); + }, 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 { + UserInfoPartial userInfo = PasswordlessQueries.getUserById_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + if (userInfo == null) { + throw new UnknownUserIdException(); + } + + 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, recipe_id, time_joined)" - + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; + + "(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()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, userInfo.id); - pst.setString(4, PASSWORDLESS.toString()); - pst.setLong(5, userInfo.timeJoined); + pst.setString(4, accountLinkingInfo.primaryUserId); + pst.setBoolean(5, accountLinkingInfo.isLinked); + pst.setString(6, PASSWORDLESS.toString()); + pst.setLong(7, userInfo.timeJoined); + pst.setLong(8, userInfo.timeJoined); }); + + 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 DO NOTHING"; + + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT " + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getPasswordlessUserToTenantTable(), null, "pkey") + + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); @@ -810,7 +961,8 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC } } - public static boolean removeUserIdFromTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) + public static boolean removeUserIdFromTenant_Transaction(Start start, Connection sqlCon, + TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { { // all_auth_recipe_users String QUERY = "DELETE FROM " + getConfig(start).getUsersTable() @@ -828,42 +980,124 @@ public static boolean removeUserIdFromTenant_Transaction(Start start, Connection // automatically deleted from passwordless_user_to_tenant because of foreign key constraint } - private static UserInfo userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, UserInfoPartial userInfo) + private static UserInfoPartial fillUserInfoWithVerified_transaction(Start start, + Connection sqlCon, + AppIdentifier appIdentifier, + UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, appIdentifier, Arrays.asList(userInfo)).get(0); + return fillUserInfoWithVerified_transaction(start, sqlCon, appIdentifier, List.of(userInfo)).get(0); + } + + private static List fillUserInfoWithVerified_transaction(Start start, + Connection sqlCon, + AppIdentifier appIdentifier, + List userInfos) + throws SQLException, StorageQueryException { + List userIdsAndEmails = new ArrayList<>(); + for (UserInfoPartial userInfo : userInfos) { + if (userInfo.email == null) { + // phone number, so we mark it as verified + userInfo.verified = true; + } else { + userIdsAndEmails.add(new EmailVerificationQueries.UserIdAndEmail(userInfo.id, userInfo.email)); + } + } + List userIdsThatAreVerified = EmailVerificationQueries.isEmailVerified_transaction(start, sqlCon, + appIdentifier, + userIdsAndEmails); + Set verifiedUserIdsSet = new HashSet<>(userIdsThatAreVerified); + for (UserInfoPartial userInfo : userInfos) { + if (userInfo.verified != null) { + // this means phone number + assert (userInfo.email == null); + continue; + } + if (verifiedUserIdsSet.contains(userInfo.id)) { + userInfo.verified = true; + } else { + userInfo.verified = false; + } } + return userInfos; } - private static List userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, List userInfos) + private static List fillUserInfoWithVerified(Start start, + AppIdentifier appIdentifier, + List userInfos) throws SQLException, StorageQueryException { - try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); + List userIdsAndEmails = new ArrayList<>(); + for (UserInfoPartial userInfo : userInfos) { + if (userInfo.email == null) { + // phone number, so we mark it as verified + userInfo.verified = true; + } else { + userIdsAndEmails.add(new EmailVerificationQueries.UserIdAndEmail(userInfo.id, userInfo.email)); + } } + List userIdsThatAreVerified = EmailVerificationQueries.isEmailVerified(start, + appIdentifier, + userIdsAndEmails); + Set verifiedUserIdsSet = new HashSet<>(userIdsThatAreVerified); + for (UserInfoPartial userInfo : userInfos) { + if (userInfo.verified != null) { + // this means phone number + assert (userInfo.email == null); + continue; + } + if (verifiedUserIdsSet.contains(userInfo.id)) { + userInfo.verified = true; + } else { + userInfo.verified = false; + } + } + return userInfos; } - private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) + private static UserInfoPartial fillUserInfoWithTenantIds_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - return userInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, Arrays.asList(userInfo)).get(0); + return fillUserInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, List.of(userInfo)).get(0); } - private static List userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, List userInfos) + private static List fillUserInfoWithTenantIds_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userInfos) throws SQLException, StorageQueryException { String[] userIds = new String[userInfos.size()]; for (int i = 0; i < userInfos.size(); i++) { userIds[i] = userInfos.get(i).id; } - Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, appIdentifier, userIds); - List result = new ArrayList<>(); + Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, + appIdentifier, + userIds); + List result = new ArrayList<>(); for (UserInfoPartial userInfo : userInfos) { - result.add(new UserInfo(userInfo.id, userInfo.email, userInfo.phoneNumber, userInfo.timeJoined, - tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]))); + userInfo.tenantIds = tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]); + } + return userInfos; + } + + private static List fillUserInfoWithTenantIds(Start start, + AppIdentifier appIdentifier, + List userInfos) + throws SQLException, StorageQueryException { + String[] userIds = new String[userInfos.size()]; + for (int i = 0; i < userInfos.size(); i++) { + userIds[i] = userInfos.get(i).id; } - return result; + Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds(start, + appIdentifier, + userIds); + List result = new ArrayList<>(); + for (UserInfoPartial userInfo : userInfos) { + userInfo.tenantIds = tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]); + } + return userInfos; } private static class PasswordlessDeviceRowMapper implements RowMapper { @@ -906,6 +1140,9 @@ private static class UserInfoPartial { public final long timeJoined; public final String email; public final String phoneNumber; + public String[] tenantIds; + public Boolean verified; + public Boolean isPrimary; UserInfoPartial(String id, @Nullable String email, @Nullable String phoneNumber, long timeJoined) { this.id = id.trim(); @@ -918,6 +1155,13 @@ private static class UserInfoPartial { this.email = email; this.phoneNumber = phoneNumber; } + + public LoginMethod toLoginMethod() { + assert (tenantIds != null); + assert (verified != null); + return new LoginMethod(id, timeJoined, verified, new LoginMethod.PasswordlessInfo(email, phoneNumber), + tenantIds); + } } private static class UserInfoRowMapper implements RowMapper { @@ -937,7 +1181,6 @@ public UserInfoPartial map(ResultSet result) throws Exception { } } - private static class UserInfoWithTenantId { public final String userId; public final String tenantId; 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 928fbd66..d6685638 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java @@ -124,19 +124,44 @@ public static void createNewSession(Start start, TenantIdentifier tenantIdentifi public static SessionInfo getSessionInfo_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String sessionHandle) throws SQLException, StorageQueryException { - String QUERY = "SELECT session_handle, user_id, refresh_token_hash_2, session_data, expires_at, " - + "created_at_time, jwt_user_payload, use_static_key FROM " + getConfig(start).getSessionInfoTable() - + " WHERE app_id = ? AND tenant_id = ? AND session_handle = ? FOR UPDATE"; - return execute(con, QUERY, pst -> { + // we do this as two separate queries and not one query with left join cause psql does not + // support left join with for update if the right table returns null. + + String QUERY = + "SELECT session_handle, user_id, refresh_token_hash_2, session_data, " + + "expires_at, created_at_time, jwt_user_payload, use_static_key FROM " + + getConfig(start).getSessionInfoTable() + + " WHERE app_id = ? AND tenant_id = ? AND session_handle = ? FOR UPDATE"; + SessionInfo sessionInfo = execute(con, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, sessionHandle); }, result -> { if (result.next()) { - return SessionInfoRowMapper.getInstance().mapOrThrow(result); + return SessionInfoRowMapper.getInstance().mapOrThrow(result, false); } return null; }); + + if (sessionInfo == null) { + return null; + } + + QUERY = "SELECT primary_or_recipe_user_id FROM " + getConfig(start).getUsersTable() + + " WHERE app_id = ? AND user_id = ?"; + + return execute(con, QUERY, pst -> { + pst.setString(1, tenantIdentifier.getAppId()); + pst.setString(2, sessionInfo.recipeUserId); + }, result -> { + if (result.next()) { + String primaryUserId = result.getString("primary_or_recipe_user_id"); + if (primaryUserId != null) { + sessionInfo.userId = primaryUserId; + } + } + return sessionInfo; + }); } public static void updateSessionInfo_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, @@ -208,6 +233,18 @@ public static void deleteSessionsOfUser(Start start, AppIdentifier appIdentifier }); } + public static void deleteSessionsOfUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, + String userId) + throws SQLException, StorageQueryException { + String QUERY = "DELETE FROM " + getConfig(start).getSessionInfoTable() + + " WHERE app_id = ? AND user_id = ?"; + + update(sqlCon, QUERY.toString(), pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } + public static boolean deleteSessionsOfUser(Start start, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getSessionInfoTable() @@ -311,16 +348,24 @@ public static int updateSession(Start start, TenantIdentifier tenantIdentifier, public static SessionInfo getSession(Start start, TenantIdentifier tenantIdentifier, String sessionHandle) throws SQLException, StorageQueryException { - String QUERY = "SELECT session_handle, user_id, refresh_token_hash_2, session_data, expires_at, " - + "created_at_time, jwt_user_payload, use_static_key FROM " + getConfig(start).getSessionInfoTable() - + " WHERE app_id = ? AND tenant_id = ? AND session_handle = ?"; + String QUERY = + "SELECT sess.session_handle, sess.user_id, sess.refresh_token_hash_2, sess.session_data, sess" + + ".expires_at, " + + + "sess.created_at_time, sess.jwt_user_payload, sess.use_static_key, users" + + ".primary_or_recipe_user_id FROM " + + getConfig(start).getSessionInfoTable() + + " AS sess LEFT JOIN " + getConfig(start).getUsersTable() + + " as users ON sess.app_id = users.app_id AND sess.user_id = users.user_id WHERE sess.app_id =" + + " ? AND " + + "sess.tenant_id = ? AND sess.session_handle = ?"; return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, sessionHandle); }, result -> { if (result.next()) { - return SessionInfoRowMapper.getInstance().mapOrThrow(result); + return SessionInfoRowMapper.getInstance().mapOrThrow(result, true); } return null; }); @@ -372,7 +417,7 @@ public static void removeAccessTokenSigningKeysBefore(Start start, AppIdentifier }); } - static class SessionInfoRowMapper implements RowMapper { + static class SessionInfoRowMapper { public static final SessionInfoRowMapper INSTANCE = new SessionInfoRowMapper(); private SessionInfoRowMapper() { @@ -382,14 +427,23 @@ private static SessionInfoRowMapper getInstance() { return INSTANCE; } - @Override - public SessionInfo map(ResultSet result) throws Exception { + public SessionInfo mapOrThrow(ResultSet result, boolean hasPrimaryOrRecipeUserId) throws StorageQueryException { JsonParser jp = new JsonParser(); - return new SessionInfo(result.getString("session_handle"), result.getString("user_id"), - result.getString("refresh_token_hash_2"), - jp.parse(result.getString("session_data")).getAsJsonObject(), result.getLong("expires_at"), - jp.parse(result.getString("jwt_user_payload")).getAsJsonObject(), - result.getLong("created_at_time"), result.getBoolean("use_static_key")); + // if result.getString("primary_or_recipe_user_id") is null, it will be handled by SessionInfo + // constructor + try { + return new SessionInfo(result.getString("session_handle"), + hasPrimaryOrRecipeUserId ? result.getString("primary_or_recipe_user_id") : + result.getString("user_id"), + result.getString("user_id"), + result.getString("refresh_token_hash_2"), + jp.parse(result.getString("session_data")).getAsJsonObject(), + result.getLong("expires_at"), + jp.parse(result.getString("jwt_user_payload")).getAsJsonObject(), + result.getLong("created_at_time"), result.getBoolean("use_static_key")); + } catch (Exception e) { + throw new StorageQueryException(e); + } } } 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 d904b7b1..2a37c9dc 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java @@ -17,21 +17,25 @@ package io.supertokens.storage.postgresql.queries; 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.UserInfo; +import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException; 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 org.jetbrains.annotations.NotNull; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.stream.Collectors; import static io.supertokens.pluginInterface.RECIPE_ID.THIRD_PARTY; import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; @@ -53,7 +57,8 @@ static String getQueryToCreateUsersTable(Start start) { + "time_joined BIGINT NOT NULL," + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, "user_id", "fkey") + " FOREIGN KEY(app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE," + + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + + " (app_id, user_id) ON DELETE CASCADE," + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, null, "pkey") + " PRIMARY KEY (app_id, user_id)" + ");"; @@ -80,41 +85,48 @@ static String getQueryToCreateThirdPartyUserToTenantTable(Start start) { + "user_id CHAR(36) NOT NULL," + "third_party_id VARCHAR(28) NOT NULL," + "third_party_user_id VARCHAR(256) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, "third_party_user_id", "key") + + "CONSTRAINT " + + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, "third_party_user_id", "key") + " UNIQUE (app_id, tenant_id, third_party_id, third_party_user_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, null, "pkey") + " PRIMARY KEY (app_id, tenant_id, user_id)," + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, "user_id", "fkey") + " FOREIGN KEY (app_id, tenant_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getUsersTable() + "(app_id, tenant_id, user_id) ON DELETE CASCADE" + + " REFERENCES " + Config.getConfig(start).getUsersTable() + + "(app_id, tenant_id, user_id) ON DELETE CASCADE" + ");"; // @formatter:on } - public static UserInfo signUp(Start start, TenantIdentifier tenantIdentifier, String id, String email, UserInfo.ThirdParty thirdParty, long timeJoined) + public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIdentifier, String id, String email, + LoginMethod.ThirdParty thirdParty, long timeJoined) throws StorageQueryException, StorageTransactionLogicException { return start.startTransaction(con -> { Connection sqlCon = (Connection) con.getConnection(); try { { // app_id_to_user_id String QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable() - + "(app_id, user_id, recipe_id)" + " VALUES(?, ?, ?)"; + + "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)"; update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, id); - pst.setString(3, THIRD_PARTY.toString()); + pst.setString(3, id); + pst.setString(4, THIRD_PARTY.toString()); }); } { // all_auth_recipe_users String QUERY = "INSERT INTO " + getConfig(start).getUsersTable() - + "(app_id, tenant_id, user_id, recipe_id, time_joined)" + " VALUES(?, ?, ?, ?, ?)"; + + "(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()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, id); - pst.setString(4, THIRD_PARTY.toString()); - pst.setLong(5, timeJoined); + pst.setString(4, id); + pst.setString(5, THIRD_PARTY.toString()); + pst.setLong(6, timeJoined); + pst.setLong(7, timeJoined); }); } @@ -145,9 +157,11 @@ public static UserInfo signUp(Start start, TenantIdentifier tenantIdentifier, St }); } - UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), new UserInfoPartial(id, email, thirdParty, timeJoined)); + UserInfoPartial userInfo = new UserInfoPartial(id, email, thirdParty, timeJoined); + fillUserInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); + fillUserInfoWithVerified_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userInfo); sqlCon.commit(); - return userInfo; + return AuthRecipeUserInfo.create(id, false, userInfo.toLoginMethod()); } catch (SQLException throwables) { throw new StorageTransactionLogicException(throwables); @@ -155,69 +169,125 @@ public static UserInfo signUp(Start start, TenantIdentifier tenantIdentifier, St }); } - public static void deleteUser(Start start, AppIdentifier appIdentifier, String userId) - throws StorageQueryException, StorageTransactionLogicException { - start.startTransaction(con -> { - Connection sqlCon = (Connection) con.getConnection(); - try { - { - String QUERY = "DELETE FROM " + getConfig(start).getAppIdToUserIdTable() - + " WHERE app_id = ? AND user_id = ?"; + public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, + String userId, boolean deleteUserIdMappingToo) + throws StorageQueryException, SQLException { + if (deleteUserIdMappingToo) { + String QUERY = "DELETE FROM " + getConfig(start).getAppIdToUserIdTable() + + " WHERE app_id = ? AND user_id = ?"; - update(sqlCon, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); - }); - } + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } else { + { + String QUERY = "DELETE FROM " + getConfig(start).getUsersTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } - sqlCon.commit(); - } catch (SQLException throwables) { - throw new StorageTransactionLogicException(throwables); + { + String QUERY = "DELETE FROM " + getConfig(start).getThirdPartyUsersTable() + + " WHERE app_id = ? AND user_id = ?"; + update(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); } - return null; + } + } + + public static List lockEmail_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String email) throws SQLException, StorageQueryException { + String QUERY = "SELECT tp.user_id as user_id " + + "FROM " + getConfig(start).getThirdPartyUsersTable() + " AS tp" + + " WHERE tp.app_id = ? AND tp.email = ? FOR UPDATE"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, email); + }, result -> { + List finalResult = new ArrayList<>(); + while (result.next()) { + finalResult.add(result.getString("user_id")); + } + return finalResult; }); } - public static UserInfo getThirdPartyUserInfoUsingId(Start start, AppIdentifier appIdentifier, String userId) + public static List lockThirdPartyInfoAndTenant_Transaction(Start start, Connection con, + AppIdentifier appIdentifier, + String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { - String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined FROM " - + getConfig(start).getThirdPartyUsersTable() + " WHERE app_id = ? AND user_id = ?"; + String QUERY = "SELECT user_id " + + " FROM " + getConfig(start).getThirdPartyUsersTable() + + " WHERE app_id = ? AND third_party_id = ? AND third_party_user_id = ? FOR UPDATE"; - UserInfoPartial userInfo = execute(start, QUERY.toString(), pst -> { + return execute(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, userId); + pst.setString(2, thirdPartyId); + pst.setString(3, thirdPartyUserId); }, result -> { - if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); + List finalResult = new ArrayList<>(); + while (result.next()) { + finalResult.add(result.getString("user_id")); } - return null; + return finalResult; }); - return userInfoWithTenantIds(start, appIdentifier, userInfo); } - public static List getUsersInfoUsingIdList(Start start, AppIdentifier appIdentifier, List ids) + public static List getUsersInfoUsingIdList(Start start, Set ids, + 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 - StringBuilder QUERY = new StringBuilder( - "SELECT user_id, third_party_id, third_party_user_id, email, time_joined " - + "FROM " + getConfig(start).getThirdPartyUsersTable()); - QUERY.append(" WHERE app_id = ? AND user_id IN ("); - for (int i = 0; i < ids.size(); i++) { - QUERY.append("?"); - if (i != ids.size() - 1) { - // not the last element - QUERY.append(","); + String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined " + + "FROM " + getConfig(start).getThirdPartyUsersTable() + " WHERE user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(ids.size()) + ") AND app_id = ?"; + + List userInfos = execute(start, QUERY, pst -> { + int index = 1; + for (String id : ids) { + pst.setString(index, id); + index++; } + pst.setString(index, appIdentifier.getAppId()); + }, result -> { + List finalResult = new ArrayList<>(); + while (result.next()) { + finalResult.add(UserInfoRowMapper.getInstance().mapOrThrow(result)); + } + return finalResult; + }); + + try (Connection con = ConnectionPool.getConnection(start)) { + fillUserInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); + fillUserInfoWithVerified_transaction(start, con, appIdentifier, userInfos); } - QUERY.append(")"); + return userInfos.stream().map(UserInfoPartial::toLoginMethod).collect(Collectors.toList()); + } + return Collections.emptyList(); + } - List userInfos = execute(start, QUERY.toString(), pst -> { - pst.setString(1, appIdentifier.getAppId()); - for (int i = 0; i < ids.size(); i++) { - // i+2 cause this starts with 1 and not 0, and 1 is appId - pst.setString(i + 2, ids.get(i)); + public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, + AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { + if (ids.size() > 0) { + String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined " + + "FROM " + getConfig(start).getThirdPartyUsersTable() + " WHERE user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(ids.size()) + ") AND app_id = ?"; + + List userInfos = execute(con, QUERY, pst -> { + int index = 1; + for (String id : ids) { + pst.setString(index, id); + index++; } + pst.setString(index, appIdentifier.getAppId()); }, result -> { List finalResult = new ArrayList<>(); while (result.next()) { @@ -225,36 +295,82 @@ public static List getUsersInfoUsingIdList(Start start, AppIdentifier } return finalResult; }); - return userInfoWithTenantIds(start, appIdentifier, userInfos); + + fillUserInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); + fillUserInfoWithVerified_transaction(start, con, appIdentifier, userInfos); + return userInfos.stream().map(UserInfoPartial::toLoginMethod).collect(Collectors.toList()); } return Collections.emptyList(); } - public static UserInfo getThirdPartyUserInfoUsingId(Start start, TenantIdentifier tenantIdentifier, - String thirdPartyId, String thirdPartyUserId) + + public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifier appIdentifier, + String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { - String QUERY = "SELECT tp_users.user_id as user_id, tp_users.third_party_id as third_party_id, " - + "tp_users.third_party_user_id as third_party_user_id, tp_users.email as email, " - + "tp_users.time_joined as time_joined " - + "FROM " + getConfig(start).getThirdPartyUserToTenantTable() + " AS tp_users_to_tenant " - + "JOIN " + getConfig(start).getThirdPartyUsersTable() + " AS tp_users " - + "ON tp_users.app_id = tp_users_to_tenant.app_id AND tp_users.user_id = tp_users_to_tenant.user_id " - + "WHERE tp_users_to_tenant.app_id = ? AND tp_users_to_tenant.tenant_id = ? " - + "AND tp_users_to_tenant.third_party_id = ? AND tp_users_to_tenant.third_party_user_id = ?"; + 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 = ? AND tp.third_party_user_id = ?"; - UserInfoPartial userInfo = execute(start, QUERY, pst -> { + return execute(start, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, thirdPartyId); + pst.setString(3, thirdPartyUserId); + }, result -> { + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); + } + return userIds; + }); + } + + public static List listUserIdsByThirdPartyInfo_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String thirdPartyId, String thirdPartyUserId) + throws SQLException, StorageQueryException { + + 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 = ? AND tp.third_party_user_id = ?"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, thirdPartyId); + pst.setString(3, thirdPartyUserId); + }, 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 { + + String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + + "FROM " + getConfig(start).getThirdPartyUserToTenantTable() + " 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.tenant_id = ? AND tp.third_party_id = ? AND tp.third_party_user_id = ?"; + + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, thirdPartyId); pst.setString(4, thirdPartyUserId); }, result -> { if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); + return result.getString("user_id"); } return null; }); - return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); } public static void updateUserEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, @@ -271,32 +387,12 @@ public static void updateUserEmail_Transaction(Start start, Connection con, AppI }); } - public static UserInfo getUserInfoUsingId_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String thirdPartyId, - String thirdPartyUserId) - throws SQLException, StorageQueryException { - - String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined FROM " - + getConfig(start).getThirdPartyUsersTable() - + " WHERE app_id = ? AND third_party_id = ? AND third_party_user_id = ? FOR UPDATE"; - UserInfoPartial userInfo = execute(con, QUERY, pst -> { - pst.setString(1, appIdentifier.getAppId()); - pst.setString(2, thirdPartyId); - pst.setString(3, thirdPartyUserId); - }, result -> { - if (result.next()) { - return UserInfoRowMapper.getInstance().mapOrThrow(result); - } - return null; - }); - return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfo); - } - - private static UserInfoPartial getUserInfoUsingUserId(Start start, Connection con, + private static UserInfoPartial getUserInfoUsingUserId_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) 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 + // we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id + // table String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined FROM " + getConfig(start).getThirdPartyUsersTable() + " WHERE app_id = ? AND user_id = ?"; @@ -311,55 +407,87 @@ private static UserInfoPartial getUserInfoUsingUserId(Start start, Connection co }); } - public static UserInfo[] getThirdPartyUsersByEmail(Start start, TenantIdentifier tenantIdentifier, - @NotNull String email) - throws SQLException, StorageQueryException { - - String QUERY = "SELECT tp_users.user_id as user_id, tp_users.third_party_id as third_party_id, " - + "tp_users.third_party_user_id as third_party_user_id, tp_users.email as email, " - + "tp_users.time_joined as time_joined " - + "FROM " + getConfig(start).getThirdPartyUsersTable() + " AS tp_users " - + "JOIN " + getConfig(start).getThirdPartyUserToTenantTable() + " AS tp_users_to_tenant " - + "ON tp_users.app_id = tp_users_to_tenant.app_id AND tp_users.user_id = tp_users_to_tenant.user_id " - + "WHERE tp_users_to_tenant.app_id = ? AND tp_users_to_tenant.tenant_id = ? AND tp_users.email = ? " - + "ORDER BY time_joined"; - - List userInfos = execute(start, QUERY.toString(), pst -> { + public static List getPrimaryUserIdUsingEmail(Start start, + TenantIdentifier tenantIdentifier, String email) + throws StorageQueryException, SQLException { + 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" + + " JOIN " + getConfig(start).getThirdPartyUserToTenantTable() + " AS tp_tenants" + + " ON tp_tenants.app_id = all_users.app_id AND tp_tenants.user_id = all_users.user_id" + + " WHERE tp.app_id = ? AND tp_tenants.tenant_id = ? AND tp.email = ?"; + + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, email); }, result -> { - List finalResult = new ArrayList<>(); + List finalResult = new ArrayList<>(); while (result.next()) { - finalResult.add(UserInfoRowMapper.getInstance().mapOrThrow(result)); + finalResult.add(result.getString("user_id")); } return finalResult; }); - return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfos).toArray(new UserInfo[0]); } - public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) - throws SQLException, StorageQueryException { - UserInfoPartial userInfo = ThirdPartyQueries.getUserInfoUsingUserId(start, sqlCon, + public static List getPrimaryUserIdUsingEmail_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).getThirdPartyUsersTable() + " AS tp" + + " JOIN " + getConfig(start).getAppIdToUserIdTable() + " 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.email = ?"; + + return execute(con, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, email); + }, result -> { + List finalResult = new ArrayList<>(); + while (result.next()) { + finalResult.add(result.getString("user_id")); + } + return finalResult; + }); + } + + public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, + TenantIdentifier tenantIdentifier, String userId) + throws SQLException, StorageQueryException, UnknownUserIdException { + UserInfoPartial userInfo = ThirdPartyQueries.getUserInfoUsingUserId_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); + if (userInfo == null) { + throw new UnknownUserIdException(); + } + + 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, recipe_id, time_joined)" - + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING"; + + "(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()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, userInfo.id); - pst.setString(4, THIRD_PARTY.toString()); - pst.setLong(5, userInfo.timeJoined); + pst.setString(4, accountLinkingInfo.primaryUserId); + pst.setBoolean(5, accountLinkingInfo.isLinked); + pst.setString(6, THIRD_PARTY.toString()); + pst.setLong(7, userInfo.timeJoined); + pst.setLong(8, userInfo.timeJoined); }); + + 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 DO NOTHING"; + + " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT " + + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getThirdPartyUserToTenantTable(), null, "pkey") + + " DO NOTHING"; int numRows = update(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); @@ -372,7 +500,8 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC } } - public static boolean removeUserIdFromTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) + public static boolean removeUserIdFromTenant_Transaction(Start start, Connection sqlCon, + TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { { // all_auth_recipe_users String QUERY = "DELETE FROM " + getConfig(start).getUsersTable() @@ -390,56 +519,84 @@ public static boolean removeUserIdFromTenant_Transaction(Start start, Connection // automatically deleted from thirdparty_user_to_tenant because of foreign key constraint } - private static UserInfo userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, UserInfoPartial userInfo) + private static UserInfoPartial fillUserInfoWithVerified_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, appIdentifier, Arrays.asList(userInfo)).get(0); - } + return fillUserInfoWithVerified_transaction(start, sqlCon, appIdentifier, List.of(userInfo)).get(0); } - private static List userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, List userInfos) + private static List fillUserInfoWithVerified_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userInfos) throws SQLException, StorageQueryException { - try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); + List userIdsAndEmails = new ArrayList<>(); + for (UserInfoPartial userInfo : userInfos) { + userIdsAndEmails.add(new EmailVerificationQueries.UserIdAndEmail(userInfo.id, userInfo.email)); + } + List userIdsThatAreVerified = EmailVerificationQueries.isEmailVerified_transaction(start, sqlCon, + appIdentifier, + userIdsAndEmails); + Set verifiedUserIdsSet = new HashSet<>(userIdsThatAreVerified); + for (UserInfoPartial userInfo : userInfos) { + if (verifiedUserIdsSet.contains(userInfo.id)) { + userInfo.verified = true; + } else { + userInfo.verified = false; + } } + return userInfos; } - private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) + private static UserInfoPartial fillUserInfoWithTenantIds_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - return userInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, Arrays.asList(userInfo)).get(0); + return fillUserInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, List.of(userInfo)).get(0); } - private static List userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, List userInfos) + private static List fillUserInfoWithTenantIds_transaction(Start start, Connection sqlCon, + AppIdentifier appIdentifier, + List userInfos) throws SQLException, StorageQueryException { String[] userIds = new String[userInfos.size()]; for (int i = 0; i < userInfos.size(); i++) { userIds[i] = userInfos.get(i).id; } - Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, appIdentifier, userIds); - List result = new ArrayList<>(); + Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, + appIdentifier, + userIds); for (UserInfoPartial userInfo : userInfos) { - result.add(new UserInfo(userInfo.id, userInfo.email, userInfo.thirdParty, userInfo.timeJoined, - tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]))); + userInfo.tenantIds = tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]); } - - return result; + return userInfos; } private static class UserInfoPartial { public final String id; public final String email; - public final UserInfo.ThirdParty thirdParty; + public final LoginMethod.ThirdParty thirdParty; public final long timeJoined; + public String[] tenantIds; + public Boolean verified; + public Boolean isPrimary; - public UserInfoPartial(String id, String email, UserInfo.ThirdParty thirdParty, long timeJoined) { + public UserInfoPartial(String id, String email, LoginMethod.ThirdParty thirdParty, long timeJoined) { this.id = id.trim(); this.email = email; this.thirdParty = thirdParty; this.timeJoined = timeJoined; } + + public LoginMethod toLoginMethod() { + assert (tenantIds != null); + assert (verified != null); + return new LoginMethod(id, timeJoined, verified, email, + new LoginMethod.ThirdParty(thirdParty.id, thirdParty.userId), tenantIds); + } } private static class UserInfoRowMapper implements RowMapper { @@ -455,7 +612,7 @@ private static UserInfoRowMapper getInstance() { @Override public UserInfoPartial map(ResultSet result) throws Exception { return new UserInfoPartial(result.getString("user_id"), result.getString("email"), - new UserInfo.ThirdParty(result.getString("third_party_id"), + new LoginMethod.ThirdParty(result.getString("third_party_id"), result.getString("third_party_user_id")), result.getLong("time_joined")); } 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 cc600818..a32dccb7 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java @@ -25,6 +25,7 @@ import io.supertokens.storage.postgresql.utils.Utils; import javax.annotation.Nullable; +import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -216,6 +217,56 @@ public static boolean updateOrDeleteExternalUserIdInfoWithExternalUserId(Start s return rowUpdated > 0; } + 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 = ?"; + return execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }, result -> { + if (result.next()) { + return UserIdMappingRowMapper.getInstance().mapOrThrow(result); + } + return null; + }); + } + + 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 = ?"; + + return execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }, result -> { + if (result.next()) { + return UserIdMappingRowMapper.getInstance().mapOrThrow(result); + } + return null; + }); + } + + 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 = ?)"; + + return execute(sqlCon, QUERY, pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + pst.setString(3, userId); + }, result -> { + ArrayList userIdMappingArray = new ArrayList<>(); + while (result.next()) { + userIdMappingArray.add(UserIdMappingRowMapper.getInstance().mapOrThrow(result)); + } + return userIdMappingArray.toArray(UserIdMapping[]::new); + }); + } + private static class UserIdMappingRowMapper implements RowMapper { private static final UserIdMappingRowMapper INSTANCE = new UserIdMappingRowMapper(); 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 d645bad1..1d2b6231 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java @@ -18,7 +18,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; - import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.storage.postgresql.Start; @@ -46,7 +45,7 @@ public static String getQueryToCreateUserMetadataTable(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 } @@ -56,7 +55,8 @@ public static String getQueryToCreateAppIdIndexForUserMetadataTable(Start start) + Config.getConfig(start).getUserMetadataTable() + "(app_id);"; } - public static int deleteUserMetadata(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { + public static int deleteUserMetadata(Start start, AppIdentifier appIdentifier, String userId) + throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getUserMetadataTable() + " WHERE app_id = ? AND user_id = ?"; @@ -66,7 +66,20 @@ public static int deleteUserMetadata(Start start, AppIdentifier appIdentifier, S }); } - public static int setUserMetadata_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, JsonObject metadata) + public static int deleteUserMetadata_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, + String userId) + throws SQLException, StorageQueryException { + String QUERY = "DELETE FROM " + getConfig(start).getUserMetadataTable() + + " WHERE app_id = ? AND user_id = ?"; + + return update(sqlCon, QUERY.toString(), pst -> { + pst.setString(1, appIdentifier.getAppId()); + pst.setString(2, userId); + }); + } + + public static int setUserMetadata_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + String userId, JsonObject metadata) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getUserMetadataTable() @@ -97,7 +110,8 @@ public static JsonObject getUserMetadata_Transaction(Start start, Connection con }); } - public static JsonObject getUserMetadata(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { + public static JsonObject getUserMetadata(Start start, AppIdentifier appIdentifier, String userId) + throws SQLException, StorageQueryException { String QUERY = "SELECT user_metadata FROM " + getConfig(start).getUserMetadataTable() + " WHERE app_id = ? AND user_id = ?"; return execute(start, QUERY, pst -> { 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 3069faa6..549cac86 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java @@ -19,7 +19,6 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.storage.postgresql.Start; import io.supertokens.storage.postgresql.config.Config; import io.supertokens.storage.postgresql.utils.Utils; @@ -45,7 +44,7 @@ public static String getQueryToCreateRolesTable(Start start) { + " PRIMARY KEY(app_id, role)," + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "app_id", "fkey") + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" + ");"; // @formatter:on } @@ -103,11 +102,13 @@ public static String getQueryToCreateUserRolesTable(Start start) { } public static String getQueryToCreateTenantIdIndexForUserRolesTable(Start start) { - return "CREATE INDEX IF NOT EXISTS user_roles_tenant_id_index ON " + getConfig(start).getUserRolesTable() + "(app_id, tenant_id);"; + return "CREATE INDEX IF NOT EXISTS user_roles_tenant_id_index ON " + getConfig(start).getUserRolesTable() + + "(app_id, tenant_id);"; } public static String getQueryToCreateRoleIndexForUserRolesTable(Start start) { - return "CREATE INDEX IF NOT EXISTS user_roles_app_id_role_index ON " + getConfig(start).getUserRolesTable() + "(app_id, role);"; + return "CREATE INDEX IF NOT EXISTS user_roles_app_id_role_index ON " + getConfig(start).getUserRolesTable() + + "(app_id, role);"; } public static String getQueryToCreateUserRolesRoleIndex(Start start) { @@ -116,7 +117,7 @@ public static String getQueryToCreateUserRolesRoleIndex(Start start) { } public static boolean createNewRoleOrDoNothingIfExists_Transaction(Start start, Connection con, - AppIdentifier appIdentifier, String role) + AppIdentifier appIdentifier, String role) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getRolesTable() + "(app_id, role) VALUES (?, ?) ON CONFLICT DO NOTHING;"; @@ -129,7 +130,8 @@ public static boolean createNewRoleOrDoNothingIfExists_Transaction(Start start, public static void addPermissionToRoleOrDoNothingIfExists_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String role, - String permission) throws SQLException, StorageQueryException { + String permission) + throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + getConfig(start).getUserRolesPermissionsTable() + " (app_id, role, permission) VALUES(?, ?, ?) ON CONFLICT DO NOTHING"; @@ -140,7 +142,8 @@ public static void addPermissionToRoleOrDoNothingIfExists_Transaction(Start star }); } - public static boolean deleteRole(Start start, AppIdentifier appIdentifier, String role) throws SQLException, StorageQueryException { + public static boolean deleteRole(Start start, AppIdentifier appIdentifier, String role) + throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getRolesTable() + " WHERE app_id = ? AND role = ? ;"; return update(start, QUERY, pst -> { @@ -149,7 +152,8 @@ public static boolean deleteRole(Start start, AppIdentifier appIdentifier, Strin }) == 1; } - public static boolean doesRoleExist(Start start, AppIdentifier appIdentifier, String role) throws SQLException, StorageQueryException { + public static boolean doesRoleExist(Start start, AppIdentifier appIdentifier, String role) + throws SQLException, StorageQueryException { String QUERY = "SELECT 1 FROM " + getConfig(start).getRolesTable() + " WHERE app_id = ? AND role = ?"; return execute(start, QUERY, pst -> { @@ -158,7 +162,8 @@ public static boolean doesRoleExist(Start start, AppIdentifier appIdentifier, St }, ResultSet::next); } - public static String[] getPermissionsForRole(Start start, AppIdentifier appIdentifier, String role) throws SQLException, StorageQueryException { + public static String[] getPermissionsForRole(Start start, AppIdentifier appIdentifier, String role) + throws SQLException, StorageQueryException { String QUERY = "SELECT permission FROM " + Config.getConfig(start).getUserRolesPermissionsTable() + " WHERE app_id = ? AND role = ?;"; return execute(start, QUERY, pst -> { @@ -173,7 +178,8 @@ public static String[] getPermissionsForRole(Start start, AppIdentifier appIdent }); } - public static String[] getRoles(Start start, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { + public static String[] getRoles(Start start, AppIdentifier appIdentifier) + throws SQLException, StorageQueryException { String QUERY = "SELECT role FROM " + getConfig(start).getRolesTable() + " WHERE app_id = ?"; return execute(start, QUERY, pst -> pst.setString(1, appIdentifier.getAppId()), result -> { ArrayList roles = new ArrayList<>(); @@ -247,7 +253,8 @@ public static boolean deleteRoleForUser_Transaction(Start start, Connection con, return rowUpdatedCount > 0; } - public static boolean doesRoleExist_transaction(Start start, Connection con, AppIdentifier appIdentifier, String role) + public static boolean doesRoleExist_transaction(Start start, Connection con, AppIdentifier appIdentifier, + String role) throws SQLException, StorageQueryException { String QUERY = "SELECT 1 FROM " + getConfig(start).getRolesTable() + " WHERE app_id = ? AND role = ? FOR UPDATE"; @@ -257,7 +264,8 @@ public static boolean doesRoleExist_transaction(Start start, Connection con, App }, ResultSet::next); } - public static String[] getUsersForRole(Start start, TenantIdentifier tenantIdentifier, String role) throws SQLException, StorageQueryException { + public static String[] getUsersForRole(Start start, TenantIdentifier tenantIdentifier, String role) + throws SQLException, StorageQueryException { String QUERY = "SELECT user_id FROM " + getConfig(start).getUserRolesTable() + " WHERE app_id = ? AND tenant_id = ? AND role = ? "; return execute(start, QUERY, pst -> { @@ -275,7 +283,8 @@ public static String[] getUsersForRole(Start start, TenantIdentifier tenantIdent public static boolean deletePermissionForRole_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String role, - String permission) throws SQLException, StorageQueryException { + String permission) + throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getUserRolesPermissionsTable() + " WHERE app_id = ? AND role = ? AND permission = ? "; @@ -323,7 +332,8 @@ public static String[] getRolesThatHavePermission(Start start, AppIdentifier app }); } - public static int deleteAllRolesForUser(Start start, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { + public static int deleteAllRolesForUser(Start start, TenantIdentifier tenantIdentifier, String userId) + throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getUserRolesTable() + " WHERE app_id = ? AND tenant_id = ? AND user_id = ?"; return update(start, QUERY, pst -> { @@ -333,10 +343,12 @@ public static int deleteAllRolesForUser(Start start, TenantIdentifier tenantIden }); } - public static int deleteAllRolesForUser(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { + public static int deleteAllRolesForUser_Transaction(Connection con, Start start, + AppIdentifier appIdentifier, String userId) + throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getUserRolesTable() + " WHERE app_id = ? AND user_id = ?"; - return update(start, QUERY, pst -> { + return update(con, QUERY, pst -> { pst.setString(1, appIdentifier.getAppId()); pst.setString(2, userId); }); diff --git a/src/main/java/io/supertokens/storage/postgresql/utils/Utils.java b/src/main/java/io/supertokens/storage/postgresql/utils/Utils.java index 7db2d0a5..91a58735 100644 --- a/src/main/java/io/supertokens/storage/postgresql/utils/Utils.java +++ b/src/main/java/io/supertokens/storage/postgresql/utils/Utils.java @@ -42,4 +42,15 @@ public static String getConstraintName(String schema, String prefixedTableName, constraintName.append('_').append(typeSuffix); return constraintName.toString(); } + + public static String generateCommaSeperatedQuestionMarks(int size) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < size; i++) { + builder.append("?"); + if (i != size - 1) { + builder.append(","); + } + } + return builder.toString(); + } } diff --git a/src/test/java/io/supertokens/storage/postgresql/test/AccountLinkingTests.java b/src/test/java/io/supertokens/storage/postgresql/test/AccountLinkingTests.java new file mode 100644 index 00000000..4f26a52c --- /dev/null +++ b/src/test/java/io/supertokens/storage/postgresql/test/AccountLinkingTests.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.test; + +import com.google.gson.JsonObject; +import io.supertokens.ProcessState; +import io.supertokens.authRecipe.AuthRecipe; +import io.supertokens.emailpassword.EmailPassword; +import io.supertokens.featureflag.EE_FEATURES; +import io.supertokens.featureflag.FeatureFlagTestContent; +import io.supertokens.multitenancy.Multitenancy; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; +import io.supertokens.pluginInterface.multitenancy.*; +import io.supertokens.storage.postgresql.test.httpRequest.HttpRequestForTesting; +import io.supertokens.storage.postgresql.test.httpRequest.HttpResponseException; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.webserver.WebserverAPI; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertNotNull; + +public class AccountLinkingTests { + + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void canLinkFailsIfTryingToLinkUsersAcrossDifferentStorageLayers() throws Exception { + String[] args = {"../"}; + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false); + FeatureFlagTestContent.getInstance(process.getProcess()) + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{ + EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY}); + process.startProcess(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + if (StorageLayer.isInMemDb(process.getProcess())) { + return; + } + + + JsonObject coreConfig = new JsonObject(); + StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess()) + .modifyConfigToAddANewUserPoolForTesting(coreConfig, 2); + + TenantIdentifier tenantIdentifier = new TenantIdentifier(null, null, "t1"); + Multitenancy.addNewOrUpdateAppOrTenant( + process.getProcess(), + new TenantIdentifier(null, null, null), + new TenantConfig( + tenantIdentifier, + new EmailPasswordConfig(true), + new ThirdPartyConfig(true, null), + new PasswordlessConfig(true), + coreConfig + ) + ); + + AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "test@example.com", "abcd1234"); + + AuthRecipe.createPrimaryUser(process.main, user1.getSupertokensUserId()); + + AuthRecipeUserInfo user2 = EmailPassword.signUp( + tenantIdentifier.withStorage(StorageLayer.getStorage(tenantIdentifier, process.main)), + process.getProcess(), "test2@example.com", "abcd1234"); + + try { + Map params = new HashMap<>(); + params.put("recipeUserId", user2.getSupertokensUserId()); + params.put("primaryUserId", user1.getSupertokensUserId()); + + HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/accountlinking/user/link/check", params, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), ""); + assert (false); + } catch (HttpResponseException e) { + assert (e.statusCode == 400); + assert (e.getMessage() + .equals("Http error. Status Code: 400. Message: Cannot link users that are parts of different " + + "databases. Different pool IDs: |localhost|5432|supertokens|public AND " + + "|localhost|5432|st2|public")); + } + + + coreConfig = new JsonObject(); + coreConfig.addProperty("postgresql_connection_pool_size", 11); + + tenantIdentifier = new TenantIdentifier(null, null, "t2"); + Multitenancy.addNewOrUpdateAppOrTenant( + process.getProcess(), + new TenantIdentifier(null, null, null), + new TenantConfig( + tenantIdentifier, + new EmailPasswordConfig(true), + new ThirdPartyConfig(true, null), + new PasswordlessConfig(true), + coreConfig + ) + ); + + AuthRecipeUserInfo user3 = EmailPassword.signUp( + tenantIdentifier.withStorage(StorageLayer.getStorage(tenantIdentifier, process.main)), + process.getProcess(), "test2@example.com", "abcd1234"); + + Map params = new HashMap<>(); + params.put("recipeUserId", user3.getSupertokensUserId()); + params.put("primaryUserId", user1.getSupertokensUserId()); + + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/accountlinking/user/link/check", params, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), ""); + assert (response.get("status").getAsString().equals("OK")); + assert (!response.get("accountsAlreadyLinked").getAsBoolean()); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } +} diff --git a/src/test/java/io/supertokens/storage/postgresql/test/ExceptionParsingTest.java b/src/test/java/io/supertokens/storage/postgresql/test/ExceptionParsingTest.java index fb5130a8..7fd988ac 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/ExceptionParsingTest.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/ExceptionParsingTest.java @@ -87,7 +87,7 @@ public void thirdPartySignupExceptions() throws Exception { String thirdPartyUserId = "tp_userId"; String userEmail = "useremail@asdf.fdas"; - var tp = new io.supertokens.pluginInterface.thirdparty.UserInfo.ThirdParty(tpId, thirdPartyUserId); + var tp = new io.supertokens.pluginInterface.authRecipe.LoginMethod.ThirdParty(tpId, thirdPartyUserId); storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, tp, System.currentTimeMillis()); try { @@ -128,15 +128,18 @@ public void emailPasswordSignupExceptions() throws Exception { String pwHash = "fakehash"; String userEmail = "useremail@asdf.fdas"; - storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, + System.currentTimeMillis()); try { - storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, + System.currentTimeMillis()); throw new Exception("This should throw"); } catch (DuplicateUserIdException ex) { // expected } try { - storage.signUp(new TenantIdentifier(null, null, null), userId2, userEmail, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId2, userEmail, pwHash, + System.currentTimeMillis()); throw new Exception("This should throw"); } catch (DuplicateEmailException ex) { // expected @@ -171,8 +174,10 @@ public void updateUsersEmail_TransactionExceptions() String userEmail2 = "useremail2@asdf.fdas"; String userEmail3 = "useremail3@asdf.fdas"; - storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, System.currentTimeMillis()); - storage.signUp(new TenantIdentifier(null, null, null), userId2, userEmail2, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, + System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId2, userEmail2, pwHash, + System.currentTimeMillis()); storage.startTransaction(conn -> { try { storage.updateUsersEmail_Transaction(new AppIdentifier(null, null), conn, userId, userEmail2); @@ -211,7 +216,8 @@ public void updateIsEmailVerified_TransactionExceptions() TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); - EmailVerificationSQLStorage storage = (EmailVerificationSQLStorage) StorageLayer.getStorage(process.getProcess()); + EmailVerificationSQLStorage storage = (EmailVerificationSQLStorage) StorageLayer.getStorage( + process.getProcess()); String userId = "userId"; String userEmail = "useremail@asdf.fdas"; @@ -219,8 +225,9 @@ public void updateIsEmailVerified_TransactionExceptions() storage.startTransaction(conn -> { try { storage.updateIsEmailVerified_Transaction(new AppIdentifier(null, null), conn, userId, userEmail, - true); - storage.updateIsEmailVerified_Transaction(new AppIdentifier(null, null), conn, userId, userEmail, true); + true); + storage.updateIsEmailVerified_Transaction(new AppIdentifier(null, null), conn, userId, userEmail, + true); } catch (TenantOrAppNotFoundException e) { throw new RuntimeException(e); } @@ -280,11 +287,12 @@ public void addPasswordResetTokenExceptions() throws Exception { String pwHash = "fakehash"; String userEmail = "useremail@asdf.fdas"; - var info = new PasswordResetTokenInfo(userId, tokenHash, System.currentTimeMillis() + 10000); + var info = new PasswordResetTokenInfo(userId, tokenHash, System.currentTimeMillis() + 10000, userEmail); try { storage.addPasswordResetToken(new AppIdentifier(null, null), info); } catch (UnknownUserIdException ex) { - storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, + System.currentTimeMillis()); } storage.addPasswordResetToken(new AppIdentifier(null, null), info); try { @@ -306,7 +314,8 @@ public void addEmailVerificationTokenExceptions() throws Exception { TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); - EmailVerificationSQLStorage storage = (EmailVerificationSQLStorage) StorageLayer.getStorage(process.getProcess()); + EmailVerificationSQLStorage storage = (EmailVerificationSQLStorage) StorageLayer.getStorage( + process.getProcess()); String userId = "userId"; String tokenHash = "fakehash"; @@ -340,16 +349,19 @@ public void verifyEmailExceptions() throws Exception { String pwHash = "fakehash"; String userEmail = "useremail@asdf.fdas"; - storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, + System.currentTimeMillis()); try { - storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId, userEmail, pwHash, + System.currentTimeMillis()); throw new Exception("This should throw"); } catch (DuplicateUserIdException ex) { // expected } try { - storage.signUp(new TenantIdentifier(null, null, null), userId2, userEmail, pwHash, System.currentTimeMillis()); + storage.signUp(new TenantIdentifier(null, null, null), userId2, userEmail, pwHash, + System.currentTimeMillis()); throw new Exception("This should throw"); } catch (DuplicateEmailException ex) { // expected diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestUserPoolIdChangeBehaviour.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestUserPoolIdChangeBehaviour.java index e8c7493f..5a1d7a1f 100644 --- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestUserPoolIdChangeBehaviour.java +++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestUserPoolIdChangeBehaviour.java @@ -25,7 +25,7 @@ import io.supertokens.multitenancy.Multitenancy; import io.supertokens.multitenancy.exception.BadPermissionException; import io.supertokens.multitenancy.exception.CannotModifyBaseConfigException; -import io.supertokens.pluginInterface.emailpassword.UserInfo; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.*; @@ -91,7 +91,7 @@ public void testUsersWorkAfterUserPoolIdChanges() throws Exception { String userPoolId = tenantIdentifierWithStorage.getStorage().getUserPoolId(); - UserInfo userInfo = EmailPassword.signUp( + AuthRecipeUserInfo userInfo = EmailPassword.signUp( tenantIdentifierWithStorage, process.getProcess(), "user@example.com", "password"); coreConfig.addProperty("postgresql_host", "127.0.0.1"); @@ -108,7 +108,8 @@ public void testUsersWorkAfterUserPoolIdChanges() throws Exception { String userPoolId2 = tenantIdentifierWithStorage.getStorage().getUserPoolId(); assertNotEquals(userPoolId, userPoolId2); - UserInfo user2 = EmailPassword.signIn(tenantIdentifierWithStorage, process.getProcess(), "user@example.com", "password"); + AuthRecipeUserInfo user2 = EmailPassword.signIn(tenantIdentifierWithStorage, process.getProcess(), + "user@example.com", "password"); assertEquals(userInfo, user2); @@ -134,7 +135,7 @@ public void testUsersWorkAfterUserPoolIdChangesAndServerRestart() throws Excepti String userPoolId = tenantIdentifierWithStorage.getStorage().getUserPoolId(); - UserInfo userInfo = EmailPassword.signUp( + AuthRecipeUserInfo userInfo = EmailPassword.signUp( tenantIdentifierWithStorage, process.getProcess(), "user@example.com", "password"); coreConfig.addProperty("postgresql_host", "127.0.0.1"); @@ -157,7 +158,9 @@ public void testUsersWorkAfterUserPoolIdChangesAndServerRestart() throws Excepti String userPoolId2 = tenantIdentifierWithStorage.getStorage().getUserPoolId(); assertNotEquals(userPoolId, userPoolId2); - UserInfo user2 = EmailPassword.signIn(tenantIdentifierWithStorage, process.getProcess(), "user@example.com", "password"); + AuthRecipeUserInfo user2 = EmailPassword.signIn(tenantIdentifierWithStorage, process.getProcess(), + "user@example.com", + "password"); assertEquals(userInfo, user2); }