diff --git a/src/main/java/io/supertokens/inmemorydb/Start.java b/src/main/java/io/supertokens/inmemorydb/Start.java index 1fc7dfa90..3ae732b5a 100644 --- a/src/main/java/io/supertokens/inmemorydb/Start.java +++ b/src/main/java/io/supertokens/inmemorydb/Start.java @@ -2815,10 +2815,15 @@ public AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(AppIdentif @Override public AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo(AppIdentifier appIdentifier, - String thirdPartyId, - String thirdPartyUserId) + String thirdPartyId, + String thirdPartyUserId) throws StorageQueryException { - return null; // TODO + try { + return GeneralQueries.listPrimaryUsersByThirdPartyInfo(this, appIdentifier, + thirdPartyId, thirdPartyUserId); + } catch (SQLException e) { + throw new StorageQueryException(e); + } } @Override @@ -2829,7 +2834,7 @@ public AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(AppIden throws StorageQueryException { try { Connection sqlCon = (Connection) con.getConnection(); - return GeneralQueries.getPrimaryUsersByThirdPartyInfo_Transaction(this, sqlCon, appIdentifier, + return GeneralQueries.listPrimaryUsersByThirdPartyInfo_Transaction(this, sqlCon, appIdentifier, thirdPartyId, thirdPartyUserId); } catch (SQLException e) { throw new StorageQueryException(e); diff --git a/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java index 315ca7f44..374aba078 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java @@ -151,7 +151,8 @@ 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() + String QUERY = + "SELECT user_id, token, token_expiry, email FROM " + getConfig(start).getPasswordResetTokensTable() + " WHERE app_id = ? AND user_id = ?"; return execute(start, QUERY, pst -> { @@ -201,7 +202,8 @@ public static PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser_Trans 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() + 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()); @@ -308,7 +310,8 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden }); } - public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, String userId, boolean deleteUserIdMappingToo) + 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() @@ -348,10 +351,10 @@ public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIde } } - public static UserInfoPartial getUserInfoUsingId(Start start, Connection sqlCon, AppIdentifier appIdentifier, + private static UserInfoPartial getUserInfoUsingId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String id) throws SQLException, StorageQueryException { - // we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id - // table + // 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 = ?"; @@ -366,14 +369,47 @@ public static UserInfoPartial getUserInfoUsingId(Start start, Connection sqlCon, }); } - public static List getUsersInfoUsingIdList(Start start, Connection con, Set 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 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 = ?"; + + "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++; + } + 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(); + } + + 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; @@ -396,14 +432,10 @@ public static List getUsersInfoUsingIdList(Start start, Connection } return Collections.emptyList(); } - - public static String lockEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, - String email) throws SQLException, StorageQueryException { - // normally the query below will use a for update, but sqlite doesn't support it. - ((ConnectionWithLocks) con).lock( - appIdentifier.getAppId() + "~" + email + - Config.getConfig(start).getEmailPasswordUsersTable()); - + 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 = ?"; return execute(con, QUERY, pst -> { @@ -417,7 +449,7 @@ public static String lockEmail_Transaction(Start start, Connection con, AppIdent }); } - public static String getPrimaryUserIdUsingEmail(Start start, Connection con, TenantIdentifier tenantIdentifier, + 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 " @@ -426,7 +458,7 @@ public static String getPrimaryUserIdUsingEmail(Start start, Connection con, Ten " 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 = ?"; - return execute(con, QUERY, pst -> { + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, email); @@ -438,7 +470,7 @@ public static String getPrimaryUserIdUsingEmail(Start start, Connection con, Ten }); } - public static List getPrimaryUserIdsUsingEmail(Start start, Connection con, AppIdentifier appIdentifier, + 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 " @@ -462,7 +494,7 @@ public static List getPrimaryUserIdsUsingEmail(Start start, Connection c public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { - UserInfoPartial userInfo = EmailPasswordQueries.getUserInfoUsingId(start, sqlCon, + UserInfoPartial userInfo = EmailPasswordQueries.getUserInfoUsingId_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users @@ -543,6 +575,28 @@ private static List fillUserInfoWithVerified_transaction(Start return userInfos; } + private static List fillUserInfoWithVerified(Start start, + 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(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 UserInfoPartial fillUserInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) @@ -567,6 +621,26 @@ private static List fillUserInfoWithTenantIds_transaction(Start for (UserInfoPartial userInfo : userInfos) { 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; + } + + Map> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds(start, + appIdentifier, + userIds); + for (UserInfoPartial userInfo : userInfos) { + userInfo.tenantIds = tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]); + } + return userInfos; } diff --git a/src/main/java/io/supertokens/inmemorydb/queries/EmailVerificationQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/EmailVerificationQueries.java index 2c5d2c207..3cfc1274e 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/EmailVerificationQueries.java @@ -278,6 +278,51 @@ public static List isEmailVerified_transaction(Start start, Connection s }); } + 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!"); + } + 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 { { diff --git a/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java index a3acdb0a8..1282f9ca5 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java @@ -85,7 +85,17 @@ static String getQueryToCreateUsersTable(Start start) { + ");"; } - static String getQueryToCreateUserPaginationIndex(Start start) { + public static String getQueryToCreateUserIdIndexForUsersTable(Start start) { + return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_index ON " + + Config.getConfig(start).getUsersTable() + "(app_id, user_id);"; + } + + public static String getQueryToCreateTenantIdIndexForUsersTable(Start start) { + return "CREATE INDEX IF NOT EXISTS all_auth_recipe_tenant_id_index ON " + + Config.getConfig(start).getUsersTable() + "(app_id, tenant_id);"; + } + + static String getQueryToCreateUserPaginationIndex(Start start) { return "CREATE INDEX all_auth_recipe_users_pagination_index ON " + Config.getConfig(start).getUsersTable() + "(primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC, tenant_id DESC, app_id DESC);"; } @@ -150,7 +160,12 @@ static String getQueryToCreateKeyValueTable(Start start) { } - private static String getQueryToCreateAppIdToUserIdTable(Start start) { + static String getQueryToCreateTenantIdIndexForKeyValueTable(Start start) { + return "CREATE INDEX IF NOT EXISTS key_value_tenant_id_index ON " + + Config.getConfig(start).getKeyValueTable() + "(app_id, tenant_id);"; + } + + private static String getQueryToCreateAppIdToUserIdTable(Start start) { String appToUserTable = Config.getConfig(start).getAppIdToUserIdTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + appToUserTable + " (" @@ -548,13 +563,11 @@ public static boolean doesUserIdExist(Start start, TenantIdentifier tenantIdenti }, ResultSet::next); } - public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenantIdentifier, @NotNull Integer limit, - @NotNull String timeJoinedOrder, - @org.jetbrains.annotations.Nullable RECIPE_ID[] includeRecipeIds, - @org.jetbrains.annotations.Nullable - String userId, - @org.jetbrains.annotations.Nullable Long timeJoined, - @Nullable DashboardSearchTags dashboardSearchTags) + public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenantIdentifier, @NotNull Integer limit, + @NotNull String timeJoinedOrder, + @Nullable RECIPE_ID[] includeRecipeIds, @Nullable String userId, + @Nullable Long timeJoined, + @Nullable DashboardSearchTags dashboardSearchTags) throws SQLException, StorageQueryException { // This list will be used to keep track of the result's order from the db @@ -746,7 +759,6 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant return temp; }); } - } } else { @@ -827,7 +839,6 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant AuthRecipeUserInfo[] finalResult = new AuthRecipeUserInfo[usersFromQuery.size()]; - // we give the userId[] for each recipe to fetch all those user's details List users = getPrimaryUserInfoForUserIds(start, tenantIdentifier.toAppIdentifier(), usersFromQuery); @@ -927,10 +938,34 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(Sta PasswordlessQueries.lockPhone_Transaction(start, sqlCon, appIdentifier, phoneNumber); // now that we have locks on all the relevant tables, we can read from them safely - return listPrimaryUsersByPhoneNumberHelper(start, sqlCon, appIdentifier, phoneNumber); + 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[] getPrimaryUsersByThirdPartyInfo_Transaction(Start start, Connection sqlCon, + public static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String thirdPartyId, String thirdPartyUserId) @@ -944,7 +979,14 @@ public static AuthRecipeUserInfo[] getPrimaryUsersByThirdPartyInfo_Transaction(S thirdPartyUserId); // now that we have locks on all the relevant tables, we can read from them safely - return listPrimaryUsersByThirdPartyInfoHelper(start, sqlCon, appIdentifier, thirdPartyId, thirdPartyUserId); + 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, @@ -962,41 +1004,20 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(Start sta PasswordlessQueries.lockEmail_Transaction(start, sqlCon, appIdentifier, email); // now that we have locks on all the relevant tables, we can read from them safely - return listPrimaryUsersByEmailHelper(start, sqlCon, appIdentifier, email); - } - - public static AuthRecipeUserInfo[] listPrimaryUsersByEmail(Start start, TenantIdentifier tenantIdentifier, - String email) - throws StorageQueryException, SQLException { - try (Connection con = ConnectionPool.getConnection(start)) { - return listPrimaryUsersByEmailHelper(start, con, tenantIdentifier, email); - } - } - - public static AuthRecipeUserInfo[] listPrimaryUsersByEmailHelper(Start start, Connection con, - TenantIdentifier tenantIdentifier, - String email) - throws StorageQueryException, SQLException { List userIds = new ArrayList<>(); - String emailPasswordUserId = EmailPasswordQueries.getPrimaryUserIdUsingEmail(start, con, tenantIdentifier, - email); - if (emailPasswordUserId != null) { - userIds.add(emailPasswordUserId); - } + userIds.addAll(EmailPasswordQueries.getPrimaryUserIdsUsingEmail_Transaction(start, sqlCon, appIdentifier, + email)); - String passwordlessUserId = PasswordlessQueries.getPrimaryUserIdUsingEmail(start, con, tenantIdentifier, - email); - if (passwordlessUserId != null) { - userIds.add(passwordlessUserId); - } + userIds.addAll(PasswordlessQueries.getPrimaryUserIdsUsingEmail_Transaction(start, sqlCon, appIdentifier, + email)); - userIds.addAll(ThirdPartyQueries.getPrimaryUserIdUsingEmail(start, con, tenantIdentifier, 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, con, tenantIdentifier.toAppIdentifier(), + List result = getPrimaryUserInfoForUserIds(start, appIdentifier, userIds); // this is going to order them based on oldest that joined to newest that joined. @@ -1005,24 +1026,29 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByEmailHelper(Start start, Co return result.toArray(new AuthRecipeUserInfo[0]); } - public static AuthRecipeUserInfo[] listPrimaryUsersByEmailHelper(Start start, Connection con, - AppIdentifier appIdentifier, - String email) + public static AuthRecipeUserInfo[] listPrimaryUsersByEmail(Start start, TenantIdentifier tenantIdentifier, + String email) throws StorageQueryException, SQLException { List userIds = new ArrayList<>(); - userIds.addAll(EmailPasswordQueries.getPrimaryUserIdsUsingEmail(start, con, appIdentifier, - email)); + String emailPasswordUserId = EmailPasswordQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier, + email); + if (emailPasswordUserId != null) { + userIds.add(emailPasswordUserId); + } - userIds.addAll(PasswordlessQueries.getPrimaryUserIdsUsingEmail(start, con, appIdentifier, - email)); + String passwordlessUserId = PasswordlessQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier, + email); + if (passwordlessUserId != null) { + userIds.add(passwordlessUserId); + } - userIds.addAll(ThirdPartyQueries.getPrimaryUserIdUsingEmail(start, con, appIdentifier, email)); + userIds.addAll(ThirdPartyQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier, email)); // remove duplicates from userIds Set userIdsSet = new HashSet<>(userIds); userIds = new ArrayList<>(userIdsSet); - List result = getPrimaryUserInfoForUserIds(start, con, appIdentifier, + List result = getPrimaryUserInfoForUserIds(start, tenantIdentifier.toAppIdentifier(), userIds); // this is going to order them based on oldest that joined to newest that joined. @@ -1035,24 +1061,15 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber(Start start, TenantIdentifier tenantIdentifier, String phoneNumber) throws StorageQueryException, SQLException { - try (Connection con = ConnectionPool.getConnection(start)) { - return listPrimaryUsersByPhoneNumberHelper(start, con, tenantIdentifier, phoneNumber); - } - } - - private static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumberHelper(Start start, Connection con, - TenantIdentifier tenantIdentifier, - String phoneNumber) - throws StorageQueryException, SQLException { List userIds = new ArrayList<>(); - String passwordlessUserId = PasswordlessQueries.getPrimaryUserByPhoneNumber(start, con, tenantIdentifier, + String passwordlessUserId = PasswordlessQueries.getPrimaryUserByPhoneNumber(start, tenantIdentifier, phoneNumber); if (passwordlessUserId != null) { userIds.add(passwordlessUserId); } - List result = getPrimaryUserInfoForUserIds(start, con, tenantIdentifier.toAppIdentifier(), + List result = getPrimaryUserInfoForUserIds(start, tenantIdentifier.toAppIdentifier(), userIds); // this is going to order them based on oldest that joined to newest that joined. @@ -1061,121 +1078,100 @@ private static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumberHelper(Start st return result.toArray(new AuthRecipeUserInfo[0]); } - - private static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumberHelper(Start start, Connection con, - AppIdentifier appIdentifier, - String phoneNumber) - throws StorageQueryException, SQLException { - List userIds = new ArrayList<>(); - - String passwordlessUserId = PasswordlessQueries.getPrimaryUserByPhoneNumber(start, con, appIdentifier, - phoneNumber); - if (passwordlessUserId != null) { - userIds.add(passwordlessUserId); - } - - List result = getPrimaryUserInfoForUserIds(start, con, 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, + public static AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(Start start, + TenantIdentifier tenantIdentifier, String thirdPartyId, String thirdPartyUserId) throws StorageQueryException, SQLException { - try (Connection con = ConnectionPool.getConnection(start)) { - return listPrimaryUsersByThirdPartyInfoHelper(start, con, appIdentifier, thirdPartyId, thirdPartyUserId); - } - } - - public static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_transaction(Start start, - Connection sqlCon, - AppIdentifier appIdentifier, - String thirdPartyId, - String thirdPartyUserId) - throws StorageQueryException, SQLException { - return listPrimaryUsersByThirdPartyInfoHelper(start, sqlCon, appIdentifier, thirdPartyId, thirdPartyUserId); - } - - public static AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(Start start, - TenantIdentifier tenantIdentifier, - String thirdPartyId, - String thirdPartyUserId) - throws StorageQueryException, SQLException { - try (Connection con = ConnectionPool.getConnection(start)) { - return getPrimaryUserByThirdPartyInfoHelper(start, con, tenantIdentifier, thirdPartyId, thirdPartyUserId); - } + String userId = ThirdPartyQueries.getUserIdByThirdPartyInfo(start, tenantIdentifier, + thirdPartyId, thirdPartyUserId); + return getPrimaryUserInfoForUserId(start, tenantIdentifier.toAppIdentifier(), userId); } - private static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfoHelper(Start start, Connection con, - AppIdentifier appIdentifier, - String thirdPartyId, - String thirdPartyUserId) - throws StorageQueryException, SQLException { - - List userIds = ThirdPartyQueries.listUserIdsByThirdPartyInfo(start, con, appIdentifier, - thirdPartyId, thirdPartyUserId); - List result = getPrimaryUserInfoForUserIds(start, con, 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]); - } - - private static AuthRecipeUserInfo getPrimaryUserByThirdPartyInfoHelper(Start start, Connection con, - TenantIdentifier tenantIdentifier, - String thirdPartyId, - String thirdPartyUserId) - throws StorageQueryException, SQLException { - - String userId = ThirdPartyQueries.getUserIdByThirdPartyInfo(start, con, tenantIdentifier, - thirdPartyId, thirdPartyUserId); - if (userId != null) { - return getPrimaryUserInfoForUserId(start, con, tenantIdentifier.toAppIdentifier(), - userId); - } - return null; - } + 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_Transaction(Start start, Connection sqlCon, - AppIdentifier appIdentifier, String id) + public static AuthRecipeUserInfo getPrimaryUserInfoForUserId(Start start, AppIdentifier appIdentifier, String id) throws SQLException, StorageQueryException { - ((ConnectionWithLocks) sqlCon).lock( - appIdentifier + "~" + id + Config.getConfig(start).getUsersTable()); + 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); + } - // We use SELECT FOR UPDATE in the query below for other plugins. + 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 * FROM " + getConfig(start).getUsersTable() + " WHERE primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " + - getConfig(start).getUsersTable() + - " WHERE user_id = ? OR primary_or_recipe_user_id = ? AND app_id = ?) AND app_id = ?"; - - List allAuthUsersResult = execute(sqlCon, QUERY, pst -> { - pst.setString(1, id); - pst.setString(2, id); - pst.setString(3, appIdentifier.getAppId()); + getConfig(start).getUsersTable() + " WHERE (user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ") OR primary_or_recipe_user_id IN (" + + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + + ")) AND app_id = ?) AND 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)); + } + // 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 finalResult = new ArrayList<>(); + List parsedResult = new ArrayList<>(); while (result.next()) { - finalResult.add(new AllAuthRecipeUsersResultHolder(result.getString("user_id"), + 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 finalResult; + return parsedResult; }); - if (allAuthUsersResult.size() == 0) { - return null; - } - // 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) { @@ -1185,11 +1181,10 @@ public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start s List loginMethods = new ArrayList<>(); loginMethods.addAll( - EmailPasswordQueries.getUsersInfoUsingIdList(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); + EmailPasswordQueries.getUsersInfoUsingIdList(start, recipeUserIdsToFetch, appIdentifier)); + loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList(start, recipeUserIdsToFetch, appIdentifier)); loginMethods.addAll( - ThirdPartyQueries.getUsersInfoUsingIdList(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); - loginMethods.addAll( - PasswordlessQueries.getUsersInfoUsingIdList(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); + PasswordlessQueries.getUsersInfoUsingIdList(start, recipeUserIdsToFetch, appIdentifier)); Map recipeUserIdToLoginMethodMap = new HashMap<>(); for (LoginMethod loginMethod : loginMethods) { @@ -1197,14 +1192,13 @@ public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start s } Map userIdToAuthRecipeUserInfo = new HashMap<>(); - String pUserId = null; + for (AllAuthRecipeUsersResultHolder authRecipeUsersResultHolder : allAuthUsersResult) { String recipeUserId = authRecipeUsersResultHolder.userId; LoginMethod loginMethod = recipeUserIdToLoginMethodMap.get(recipeUserId); assert (loginMethod != null); String primaryUserId = authRecipeUsersResultHolder.primaryOrRecipeUserId; - pUserId = primaryUserId; - AuthRecipeUserInfo curr = userIdToAuthRecipeUserInfo.get(primaryUserId); + AuthRecipeUserInfo curr = userIdToAuthRecipeUserInfo.get(primaryUserId); if (curr == null) { curr = AuthRecipeUserInfo.create(primaryUserId, authRecipeUsersResultHolder.isLinkedOrIsAPrimaryUser, loginMethod); @@ -1214,47 +1208,11 @@ public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start s userIdToAuthRecipeUserInfo.put(primaryUserId, curr); } - assert (userIdToAuthRecipeUserInfo.size() == 1 && pUserId != null); - - return userIdToAuthRecipeUserInfo.get(pUserId); - } - - 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 { - try (Connection con = ConnectionPool.getConnection(start)) { - return getPrimaryUserInfoForUserId(start, con, appIdentifier, id); - } - } - - private static AuthRecipeUserInfo getPrimaryUserInfoForUserId(Start start, Connection con, - AppIdentifier appIdentifier, String id) - throws SQLException, StorageQueryException { - List ids = new ArrayList<>(); - ids.add(id); - List result = getPrimaryUserInfoForUserIds(start, con, appIdentifier, ids); - if (result.isEmpty()) { - return null; - } - return result.get(0); + return userIdToAuthRecipeUserInfo.keySet().stream().map(userIdToAuthRecipeUserInfo::get) + .collect(Collectors.toList()); } - private static List getPrimaryUserInfoForUserIds(Start start, - Connection con, + private static List getPrimaryUserInfoForUserIds_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, List userIds) throws StorageQueryException, SQLException { @@ -1274,7 +1232,7 @@ private static List getPrimaryUserInfoForUserIds(Start start Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + ")) AND app_id = ?) AND app_id = ?"; - List allAuthUsersResult = execute(con, QUERY, pst -> { + List allAuthUsersResult = execute(sqlCon, QUERY, pst -> { // IN user_id int index = 1; for (int i = 0; i < userIds.size(); i++, index++) { @@ -1309,10 +1267,10 @@ private static List getPrimaryUserInfoForUserIds(Start start List loginMethods = new ArrayList<>(); loginMethods.addAll( - EmailPasswordQueries.getUsersInfoUsingIdList(start, con, recipeUserIdsToFetch, appIdentifier)); - loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList(start, con, recipeUserIdsToFetch, appIdentifier)); + EmailPasswordQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); + loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); loginMethods.addAll( - PasswordlessQueries.getUsersInfoUsingIdList(start, con, recipeUserIdsToFetch, appIdentifier)); + PasswordlessQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier)); Map recipeUserIdToLoginMethodMap = new HashMap<>(); for (LoginMethod loginMethod : loginMethods) { @@ -1325,7 +1283,6 @@ private static List getPrimaryUserInfoForUserIds(Start start String recipeUserId = authRecipeUsersResultHolder.userId; LoginMethod loginMethod = recipeUserIdToLoginMethodMap.get(recipeUserId); assert (loginMethod != null); - String primaryUserId = authRecipeUsersResultHolder.primaryOrRecipeUserId; AuthRecipeUserInfo curr = userIdToAuthRecipeUserInfo.get(primaryUserId); if (curr == null) { @@ -1341,20 +1298,6 @@ private static List getPrimaryUserInfoForUserIds(Start start .collect(Collectors.toList()); } - private static List getPrimaryUserInfoForUserIds(Start start, - AppIdentifier appIdentifier, - List userIds) - throws StorageQueryException, SQLException { - if (userIds.size() == 0) { - return new ArrayList<>(); - } - - try (Connection con = ConnectionPool.getConnection(start)) { - return getPrimaryUserInfoForUserIds(start, con, appIdentifier, userIds); - } - - } - public static String getRecipeIdForUser_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { @@ -1364,7 +1307,6 @@ public static String getRecipeIdForUser_Transaction(Start start, Connection sqlC String QUERY = "SELECT recipe_id FROM " + getConfig(start).getAppIdToUserIdTable() + " WHERE app_id = ? AND user_id = ?"; - return execute(sqlCon, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, userId); @@ -1396,7 +1338,50 @@ public static Map> getTenantIdsForUserIds_transaction(Start return execute(sqlCon, QUERY.toString(), pst -> { for (int i = 0; i < userIds.length; i++) { - // i+1 cause this starts with 1 and not 0 + // 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()); @@ -1462,16 +1447,6 @@ public static String[] getAllTablesInTheDatabaseThatHasDataForAppId(Start start, return result.toArray(new String[0]); } - private static class UserInfoPaginationResultHolder { - String userId; - String recipeId; - - UserInfoPaginationResultHolder(String userId, String recipeId) { - this.userId = userId; - this.recipeId = recipeId; - } - } - private static class AllAuthRecipeUsersResultHolder { String userId; String tenantId; @@ -1506,4 +1481,4 @@ public KeyValueInfo map(ResultSet result) throws Exception { return new KeyValueInfo(result.getString("value"), result.getLong("created_at_time")); } } -} +} \ No newline at end of file diff --git a/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java index c2c2a2aa0..e95bc7eb1 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java @@ -433,7 +433,7 @@ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenant }); } - private static UserInfoWithTenantId[] getUserInfosWithTenant(Start start, Connection con, + 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, " @@ -463,7 +463,7 @@ private static UserInfoWithTenantId[] getUserInfosWithTenant(Start start, Connec public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIdentifier appIdentifier, String userId, boolean deleteUserIdMappingToo) throws StorageQueryException, SQLException { - UserInfoWithTenantId[] userInfos = getUserInfosWithTenant(start, sqlCon, appIdentifier, userId); + UserInfoWithTenantId[] userInfos = getUserInfosWithTenant_Transaction(start, sqlCon, appIdentifier, userId); if (deleteUserIdMappingToo) { String QUERY = "DELETE FROM " + getConfig(start).getAppIdToUserIdTable() @@ -687,7 +687,7 @@ public static PasswordlessCode getCodeByLinkCodeHash(Start start, TenantIdentifi } } - public static List getUsersInfoUsingIdList(Start start, Connection con, Set ids, + public static List getUsersInfoUsingIdList(Start start, Set ids, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { @@ -696,6 +696,36 @@ public static List getUsersInfoUsingIdList(Start start, Connection + "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++; + } + 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(); + } + + 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) { @@ -717,7 +747,7 @@ public static List getUsersInfoUsingIdList(Start start, Connection return Collections.emptyList(); } - public static UserInfoPartial getUserById(Start start, Connection sqlCon, AppIdentifier appIdentifier, + public 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 @@ -779,7 +809,7 @@ public static String lockPhone_Transaction(Start start, Connection con, }); } - public static String getPrimaryUserIdUsingEmail(Start start, Connection con, TenantIdentifier tenantIdentifier, + 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 " @@ -788,7 +818,7 @@ public static String getPrimaryUserIdUsingEmail(Start start, Connection con, Ten " 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 = ?"; - return execute(con, QUERY, pst -> { + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, email); @@ -800,7 +830,7 @@ public static String getPrimaryUserIdUsingEmail(Start start, Connection con, Ten }); } - public static List getPrimaryUserIdsUsingEmail(Start start, Connection con, AppIdentifier appIdentifier, + 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 " @@ -821,7 +851,7 @@ public static List getPrimaryUserIdsUsingEmail(Start start, Connection c }); } - public static String getPrimaryUserByPhoneNumber(Start start, Connection con, TenantIdentifier tenantIdentifier, + 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 " @@ -830,7 +860,7 @@ public static String getPrimaryUserByPhoneNumber(Start start, Connection con, Te " 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(con, QUERY, pst -> { + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, phoneNumber); @@ -842,8 +872,8 @@ public static String getPrimaryUserByPhoneNumber(Start start, Connection con, Te }); } - public static String getPrimaryUserByPhoneNumber(Start start, Connection con, AppIdentifier appIdentifier, - @Nonnull String phoneNumber) + public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier, + @Nonnull String phoneNumber) throws StorageQueryException, SQLException { String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id " + "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" + @@ -855,17 +885,18 @@ public static String getPrimaryUserByPhoneNumber(Start start, Connection con, Ap pst.setString(1, appIdentifier.getAppId()); pst.setString(2, phoneNumber); }, result -> { - if (result.next()) { - return result.getString("user_id"); + List userIds = new ArrayList<>(); + while (result.next()) { + userIds.add(result.getString("user_id")); } - return null; + return userIds; }); } public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException, SQLException { - UserInfoPartial userInfo = PasswordlessQueries.getUserById(start, sqlCon, + UserInfoPartial userInfo = PasswordlessQueries.getUserById_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users @@ -919,6 +950,38 @@ public static boolean removeUserIdFromTenant_Transaction(Start start, Connection // automatically deleted from passwordless_user_to_tenant because of foreign key constraint } + private static List fillUserInfoWithVerified(Start start, + 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(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 UserInfoPartial fillUserInfoWithVerified_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, @@ -928,6 +991,25 @@ private static UserInfoPartial fillUserInfoWithVerified_transaction(Start start, return fillUserInfoWithVerified_transaction(start, sqlCon, appIdentifier, List.of(userInfo)).get(0); } + 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); + List result = new ArrayList<>(); + for (UserInfoPartial userInfo : userInfos) { + userInfo.tenantIds = tenantIdsForUserIds.get(userInfo.id).toArray(new String[0]); + } + return userInfos; + } + private static List fillUserInfoWithVerified_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, diff --git a/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java index e38b6b336..60d53e294 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java @@ -16,6 +16,7 @@ package io.supertokens.inmemorydb.queries; +import io.supertokens.inmemorydb.ConnectionPool; import io.supertokens.inmemorydb.ConnectionWithLocks; import io.supertokens.inmemorydb.Start; import io.supertokens.inmemorydb.Utils; @@ -236,7 +237,39 @@ public static List lockThirdPartyInfo_Transaction(Start start, Connectio }); } - public static List getUsersInfoUsingIdList(Start start, Connection con, Set ids, + public static List getUsersInfoUsingIdList(Start start, 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(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); + } + return userInfos.stream().map(UserInfoPartial::toLoginMethod).collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { if (ids.size() > 0) { @@ -266,7 +299,7 @@ public static List getUsersInfoUsingIdList(Start start, Connection return Collections.emptyList(); } - public static List listUserIdsByThirdPartyInfo(Start start, Connection con, AppIdentifier appIdentifier, + public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifier appIdentifier, String thirdPartyId, String thirdPartyUserId) throws SQLException, StorageQueryException { @@ -276,6 +309,29 @@ public static List listUserIdsByThirdPartyInfo(Start start, Connection c " 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(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); @@ -289,7 +345,7 @@ public static List listUserIdsByThirdPartyInfo(Start start, Connection c }); } - public static String getUserIdByThirdPartyInfo(Start start, Connection con, TenantIdentifier tenantIdentifier, + 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 " @@ -298,7 +354,7 @@ public static String getUserIdByThirdPartyInfo(Start start, Connection con, Tena " 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(con, QUERY, pst -> { + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, thirdPartyId); @@ -311,7 +367,7 @@ public static String getUserIdByThirdPartyInfo(Start start, Connection con, Tena }); } - public static List getPrimaryUserIdUsingEmail(Start start, Connection con, + 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 " @@ -346,7 +402,7 @@ public static void updateUserEmail_Transaction(Start start, Connection con, AppI }); } - private static UserInfoPartial getUserInfoUsingUserId(Start start, Connection con, + private static UserInfoPartial getUserInfoUsingUserId_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { @@ -366,7 +422,7 @@ private static UserInfoPartial getUserInfoUsingUserId(Start start, Connection co }); } - public static List getPrimaryUserIdUsingEmail(Start start, Connection con, + 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 " @@ -377,7 +433,7 @@ public static List getPrimaryUserIdUsingEmail(Start start, Connection co " 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(con, QUERY, pst -> { + return execute(start, QUERY, pst -> { pst.setString(1, tenantIdentifier.getAppId()); pst.setString(2, tenantIdentifier.getTenantId()); pst.setString(3, email); @@ -393,7 +449,7 @@ public static List getPrimaryUserIdUsingEmail(Start start, Connection co public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) throws SQLException, StorageQueryException { - UserInfoPartial userInfo = ThirdPartyQueries.getUserInfoUsingUserId(start, sqlCon, + UserInfoPartial userInfo = ThirdPartyQueries.getUserInfoUsingUserId_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId); { // all_auth_recipe_users