diff --git a/CHANGELOG.md b/CHANGELOG.md index 4139f41ab..90743b004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [6.0.3] - 2023-07-11 + +- Fixes duplicate users in users search queries when user is associated to multiple tenants + ## [6.0.2] - 2023-07-04 - Fixes some of the session APIs to return `tenantId` diff --git a/build.gradle b/build.gradle index d053ff59d..80db5c6e0 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" } // } //} -version = "6.0.2" +version = "6.0.3" repositories { diff --git a/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java index f46b1c0e2..18649b2c7 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/EmailPasswordQueries.java @@ -18,7 +18,6 @@ import io.supertokens.inmemorydb.ConnectionPool; import io.supertokens.inmemorydb.ConnectionWithLocks; -import io.supertokens.inmemorydb.ResultSetValueExtractor; import io.supertokens.inmemorydb.Start; import io.supertokens.inmemorydb.config.Config; import io.supertokens.pluginInterface.RowMapper; @@ -28,7 +27,6 @@ import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -import org.jetbrains.annotations.NotNull; import java.sql.Connection; import java.sql.ResultSet; @@ -209,7 +207,7 @@ public static UserInfo getUserInfoUsingId_Transaction(Start start, Connection co } return null; }); - return userInfoWithTenantIds_transaction(start, con, userInfo); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfo); } public static PasswordResetTokenInfo getPasswordResetTokenInfo(Start start, AppIdentifier appIdentifier, String token) @@ -292,7 +290,7 @@ public static UserInfo signUp(Start start, TenantIdentifier tenantIdentifier, St }); } - UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, new UserInfoPartial(userId, email, passwordHash, timeJoined)); + UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), new UserInfoPartial(userId, email, passwordHash, timeJoined)); sqlCon.commit(); return userInfo; @@ -338,7 +336,7 @@ public static UserInfo getUserInfoUsingId(Start start, AppIdentifier appIdentifi } return null; }); - return userInfoWithTenantIds(start, userInfo); + return userInfoWithTenantIds(start, appIdentifier, userInfo); } public static UserInfoPartial getUserInfoUsingId(Start start, Connection sqlCon, AppIdentifier appIdentifier, String id) throws SQLException, StorageQueryException { @@ -357,13 +355,13 @@ public static UserInfoPartial getUserInfoUsingId(Start start, Connection sqlCon, }); } - public static List getUsersInfoUsingIdList(Start start, List ids) + public static List getUsersInfoUsingIdList(Start start, AppIdentifier appIdentifier, List ids) 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 user_id IN ("); + QUERY.append(" WHERE app_id = ? AND user_id IN ("); for (int i = 0; i < ids.size(); i++) { QUERY.append("?"); @@ -375,9 +373,10 @@ public static List getUsersInfoUsingIdList(Start start, List i QUERY.append(")"); List userInfos = execute(start, QUERY.toString(), pst -> { + pst.setString(1, appIdentifier.getAppId()); for (int i = 0; i < ids.size(); i++) { - // i+1 cause this starts with 1 and not 0 - pst.setString(i + 1, ids.get(i)); + // i+2 cause this starts with 1 and not 0, and 1 is appId + pst.setString(i + 2, ids.get(i)); } }, result -> { List finalResult = new ArrayList<>(); @@ -386,7 +385,7 @@ public static List getUsersInfoUsingIdList(Start start, List i } return finalResult; }); - return userInfoWithTenantIds(start, userInfos); + return userInfoWithTenantIds(start, appIdentifier, userInfos); } return Collections.emptyList(); } @@ -409,7 +408,7 @@ public static UserInfo getUserInfoUsingEmail(Start start, TenantIdentifier tenan } return null; }); - return userInfoWithTenantIds(start, userInfo); + return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); } public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, @@ -463,35 +462,35 @@ 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, UserInfoPartial userInfo) + private static UserInfo userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, Arrays.asList(userInfo)).get(0); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, Arrays.asList(userInfo)).get(0); } } - private static List userInfoWithTenantIds(Start start, List userInfos) + private static List userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, List userInfos) throws SQLException, StorageQueryException { try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, userInfos); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); } } - private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, UserInfoPartial userInfo) + private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - return userInfoWithTenantIds_transaction(start, sqlCon, Arrays.asList(userInfo)).get(0); + return userInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, Arrays.asList(userInfo)).get(0); } - private static List userInfoWithTenantIds_transaction(Start start, Connection sqlCon, List userInfos) + private static List userInfoWithTenantIds_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, userIds); + 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, diff --git a/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java index 7141a7da7..b71cf854f 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java @@ -28,6 +28,7 @@ import io.supertokens.pluginInterface.dashboard.DashboardSearchTags; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -532,6 +533,7 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant + " AS allAuthUsersTable" + " JOIN " + getConfig(start).getEmailPasswordUserToTenantTable() + " AS emailpasswordTable ON allAuthUsersTable.app_id = emailpasswordTable.app_id AND " + + "allAuthUsersTable.tenant_id = emailpasswordTable.tenant_id AND " + "allAuthUsersTable.user_id = emailpasswordTable.user_id"; // attach email tags to queries @@ -557,15 +559,14 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant // check if we should search through the thirdparty table if (dashboardSearchTags.shouldThirdPartyTableBeSearched()) { String QUERY = "SELECT allAuthUsersTable.*" + " FROM " + getConfig(start).getUsersTable() - + " AS allAuthUsersTable" + - " JOIN " + getConfig(start).getThirdPartyUsersTable() - + " AS thirdPartyTable ON allAuthUsersTable.app_id = thirdPartyTable.app_id AND" - + " allAuthUsersTable.user_id = thirdPartyTable.user_id" + + " AS allAuthUsersTable" + " JOIN " + getConfig(start).getThirdPartyUserToTenantTable() - + - " AS thirdPartyToTenantTable ON thirdPartyTable.app_id = thirdPartyToTenantTable" + - ".app_id AND" - + " thirdPartyTable.user_id = thirdPartyToTenantTable.user_id"; + + " 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() + + " AS thirdPartyTable ON thirdPartyToTenantTable.app_id = thirdPartyTable.app_id AND" + + " thirdPartyToTenantTable.user_id = thirdPartyTable.user_id"; // check if email tag is present if (dashboardSearchTags.emails != null) { @@ -630,6 +631,7 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant + " AS allAuthUsersTable" + " JOIN " + getConfig(start).getPasswordlessUserToTenantTable() + " AS passwordlessTable ON allAuthUsersTable.app_id = passwordlessTable.app_id AND" + + " allAuthUsersTable.tenant_id = passwordlessTable.tenant_id AND" + " allAuthUsersTable.user_id = passwordlessTable.user_id"; // check if email tag is present @@ -805,7 +807,7 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant // 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, recipeId, recipeIdToUserIdListMap.get(recipeId)); + tenantIdentifier.toAppIdentifier(), recipeId, recipeIdToUserIdListMap.get(recipeId)); // we fill in all the slots in finalResult based on their position in // usersFromQuery @@ -824,16 +826,16 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant } private static List getUserInfoForRecipeIdFromUserIds(Start start, - TenantIdentifier tenantIdentifier, + AppIdentifier appIdentifier, RECIPE_ID recipeId, List userIds) throws StorageQueryException, SQLException { if (recipeId == RECIPE_ID.EMAIL_PASSWORD) { - return EmailPasswordQueries.getUsersInfoUsingIdList(start, userIds); + return EmailPasswordQueries.getUsersInfoUsingIdList(start, appIdentifier, userIds); } else if (recipeId == RECIPE_ID.THIRD_PARTY) { - return ThirdPartyQueries.getUsersInfoUsingIdList(start, userIds); + return ThirdPartyQueries.getUsersInfoUsingIdList(start, appIdentifier, userIds); } else if (recipeId == RECIPE_ID.PASSWORDLESS) { - return PasswordlessQueries.getUsersByIdList(start, userIds); + return PasswordlessQueries.getUsersByIdList(start, appIdentifier, userIds); } else { throw new IllegalArgumentException("No implementation of get users for recipe: " + recipeId.toString()); } @@ -858,12 +860,12 @@ public static String getRecipeIdForUser_Transaction(Start start, Connection sqlC }); } - public static Map> getTenantIdsForUserIds_transaction(Start start, Connection sqlCon, 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 user_id IN ("); + QUERY.append(" WHERE app_id = ? AND user_id IN ("); for (int i = 0; i < userIds.length; i++) { QUERY.append("?"); @@ -875,9 +877,10 @@ public static Map> getTenantIdsForUserIds_transaction(Start QUERY.append(")"); return execute(sqlCon, QUERY.toString(), pst -> { + pst.setString(1, appIdentifier.getAppId()); for (int i = 0; i < userIds.length; i++) { - // i+1 cause this starts with 1 and not 0 - pst.setString(i + 1, userIds[i]); + // i+2 cause this starts with 1 and not 0, and 1 is appId + pst.setString(i + 2, userIds[i]); } }, result -> { Map> finalResult = new HashMap<>(); diff --git a/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java index bf24e1d70..7694189d8 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/PasswordlessQueries.java @@ -393,7 +393,7 @@ public static UserInfo createUser(Start start, TenantIdentifier tenantIdentifier pst.setString(5, phoneNumber); }); } - UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, new UserInfoPartial(id, email, phoneNumber, timeJoined)); + UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), new UserInfoPartial(id, email, phoneNumber, timeJoined)); sqlCon.commit(); return userInfo; } catch (SQLException throwables) { @@ -640,13 +640,13 @@ public static PasswordlessCode getCodeByLinkCodeHash(Start start, TenantIdentifi } } - public static List getUsersByIdList(Start start, List ids) + public static List getUsersByIdList(Start start, AppIdentifier appIdentifier, List ids) 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 user_id IN ("); + QUERY.append(" WHERE app_id = ? AND user_id IN ("); for (int i = 0; i < ids.size(); i++) { QUERY.append("?"); if (i != ids.size() - 1) { @@ -657,9 +657,10 @@ public static List getUsersByIdList(Start start, List ids) QUERY.append(")"); List userInfos = execute(start, QUERY.toString(), pst -> { + pst.setString(1, appIdentifier.getAppId()); for (int i = 0; i < ids.size(); i++) { - // i+1 cause this starts with 1 and not 0 - pst.setString(i + 1, ids.get(i)); + // i+2 cause this starts with 1 and not 0, and 1 is appId + pst.setString(i + 2, ids.get(i)); } }, result -> { List finalResult = new ArrayList<>(); @@ -668,7 +669,7 @@ public static List getUsersByIdList(Start start, List ids) } return finalResult; }); - return userInfoWithTenantIds(start, userInfos); + return userInfoWithTenantIds(start, appIdentifier, userInfos); } return Collections.emptyList(); } @@ -686,7 +687,7 @@ public static UserInfo getUserById(Start start, AppIdentifier appIdentifier, Str } return null; }); - return userInfoWithTenantIds(start, userInfo); + return userInfoWithTenantIds(start, appIdentifier, userInfo); } public static UserInfoPartial getUserById(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId) throws StorageQueryException, SQLException { @@ -725,7 +726,7 @@ public static UserInfo getUserByEmail(Start start, TenantIdentifier tenantIdenti } return null; }); - return userInfoWithTenantIds(start, userInfo); + return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); } public static UserInfo getUserByPhoneNumber(Start start, TenantIdentifier tenantIdentifier, @Nonnull String phoneNumber) @@ -747,7 +748,7 @@ public static UserInfo getUserByPhoneNumber(Start start, TenantIdentifier tenant } return null; }); - return userInfoWithTenantIds(start, userInfo); + return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); } public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) @@ -803,35 +804,35 @@ 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, UserInfoPartial userInfo) + private static UserInfo userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, Arrays.asList(userInfo)).get(0); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, Arrays.asList(userInfo)).get(0); } } - private static List userInfoWithTenantIds(Start start, List userInfos) + private static List userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, List userInfos) throws SQLException, StorageQueryException { try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, userInfos); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); } } - private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, UserInfoPartial userInfo) + private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - return userInfoWithTenantIds_transaction(start, sqlCon, Arrays.asList(userInfo)).get(0); + return userInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, Arrays.asList(userInfo)).get(0); } - private static List userInfoWithTenantIds_transaction(Start start, Connection sqlCon, List userInfos) + private static List userInfoWithTenantIds_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, userIds); + 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, diff --git a/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java index 7e1fb3ad6..5ef6ee4d8 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/ThirdPartyQueries.java @@ -135,7 +135,7 @@ public static UserInfo signUp(Start start, TenantIdentifier tenantIdentifier, St }); } - UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, new UserInfoPartial(id, email, thirdParty, timeJoined)); + UserInfo userInfo = userInfoWithTenantIds_transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), new UserInfoPartial(id, email, thirdParty, timeJoined)); sqlCon.commit(); return userInfo; @@ -182,17 +182,17 @@ public static UserInfo getThirdPartyUserInfoUsingId(Start start, AppIdentifier a } return null; }); - return userInfoWithTenantIds(start, userInfo); + return userInfoWithTenantIds(start, appIdentifier, userInfo); } - public static List getUsersInfoUsingIdList(Start start, List ids) + public static List getUsersInfoUsingIdList(Start start, AppIdentifier appIdentifier, List ids) 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 user_id IN ("); + QUERY.append(" WHERE app_id = ? AND user_id IN ("); for (int i = 0; i < ids.size(); i++) { QUERY.append("?"); @@ -204,9 +204,10 @@ public static List getUsersInfoUsingIdList(Start start, List i QUERY.append(")"); List userInfos = execute(start, QUERY.toString(), pst -> { + pst.setString(1, appIdentifier.getAppId()); for (int i = 0; i < ids.size(); i++) { - // i+1 cause this starts with 1 and not 0 - pst.setString(i + 1, ids.get(i)); + // i+2 cause this starts with 1 and not 0, and 1 is appId + pst.setString(i + 2, ids.get(i)); } }, result -> { List finalResult = new ArrayList<>(); @@ -215,7 +216,7 @@ public static List getUsersInfoUsingIdList(Start start, List i } return finalResult; }); - return userInfoWithTenantIds(start, userInfos); + return userInfoWithTenantIds(start, appIdentifier, userInfos); } return Collections.emptyList(); } @@ -244,7 +245,7 @@ public static UserInfo getThirdPartyUserInfoUsingId(Start start, TenantIdentifie } return null; }); - return userInfoWithTenantIds(start, userInfo); + return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfo); } public static void updateUserEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier, @@ -281,7 +282,7 @@ public static UserInfo getUserInfoUsingId_Transaction(Start start, Connection co } return null; }); - return userInfoWithTenantIds_transaction(start, con, userInfo); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfo); } private static UserInfoPartial getUserInfoUsingUserId(Start start, Connection con, @@ -327,7 +328,7 @@ public static UserInfo[] getThirdPartyUsersByEmail(Start start, TenantIdentifier } return finalResult; }); - return userInfoWithTenantIds(start, userInfos).toArray(new UserInfo[0]); + return userInfoWithTenantIds(start, tenantIdentifier.toAppIdentifier(), userInfos).toArray(new UserInfo[0]); } public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String userId) @@ -382,35 +383,35 @@ 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, UserInfoPartial userInfo) + private static UserInfo userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, Arrays.asList(userInfo)).get(0); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, Arrays.asList(userInfo)).get(0); } } - private static List userInfoWithTenantIds(Start start, List userInfos) + private static List userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, List userInfos) throws SQLException, StorageQueryException { try (Connection con = ConnectionPool.getConnection(start)) { - return userInfoWithTenantIds_transaction(start, con, userInfos); + return userInfoWithTenantIds_transaction(start, con, appIdentifier, userInfos); } } - private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, UserInfoPartial userInfo) + private static UserInfo userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, UserInfoPartial userInfo) throws SQLException, StorageQueryException { if (userInfo == null) return null; - return userInfoWithTenantIds_transaction(start, sqlCon, Arrays.asList(userInfo)).get(0); + return userInfoWithTenantIds_transaction(start, sqlCon, appIdentifier, Arrays.asList(userInfo)).get(0); } - private static List userInfoWithTenantIds_transaction(Start start, Connection sqlCon, List userInfos) + private static List userInfoWithTenantIds_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, userIds); + 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.thirdParty, userInfo.timeJoined, diff --git a/src/test/java/io/supertokens/test/authRecipe/MultitenantAPITest.java b/src/test/java/io/supertokens/test/authRecipe/MultitenantAPITest.java index 9552fa5ee..51056e96b 100644 --- a/src/test/java/io/supertokens/test/authRecipe/MultitenantAPITest.java +++ b/src/test/java/io/supertokens/test/authRecipe/MultitenantAPITest.java @@ -43,6 +43,7 @@ import io.supertokens.test.Utils; import io.supertokens.test.httpRequest.HttpRequestForTesting; import io.supertokens.test.httpRequest.HttpResponseException; +import io.supertokens.test.multitenant.api.TestMultitenancyAPIHelper; import io.supertokens.thirdparty.InvalidProviderConfigException; import io.supertokens.thirdparty.ThirdParty; import io.supertokens.utils.SemVer; @@ -467,6 +468,80 @@ public void testSearch() throws Exception { } createUsers(); + + for (TenantIdentifier tenant : new TenantIdentifier[]{t1, t2, t3}) { + { + String[] users = getUsers(tenant, new String[]{"user"}, null, null); + assertEquals(8, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + { + String[] users = getUsers(tenant, new String[]{"gmail.com"}, null, null); + assertEquals(4, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + { + String[] users = getUsers(tenant, null, new String[]{"+1234"}, null); + assertEquals(1, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + { + String[] users = getUsers(tenant, null, null, new String[]{"goog"}); + assertEquals(2, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + { + String[] users = getUsers(tenant, null, null, new String[]{"face"}); + assertEquals(2, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + { + String[] users = getUsers(tenant, null, null, new String[]{"goog", "face"}); + assertEquals(4, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + { + String[] users = getUsers(tenant, new String[]{"gmail.com"}, null, new String[]{"goog"}); + assertEquals(1, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + { + String[] users = getUsers(tenant, new String[]{"gmail.com"}, null, new String[]{"goog", "face"}); + assertEquals(2, users.length); + for (String user : users) { + assertTrue(tenantToUsers.get(tenant).contains(user)); + } + } + } + } + + @Test + public void testSearchWhenUsersAreSharedAcrossTenants() throws Exception { + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + createUsers(); + { // associate across tenants + for (String userId : tenantToUsers.get(t2)) { + TestMultitenancyAPIHelper.associateUserToTenant(t3, userId, process.getProcess()); + } + } + for (TenantIdentifier tenant : new TenantIdentifier[]{t1, t2, t3}) { { String[] users = getUsers(tenant, new String[]{"user"}, null, null);