diff --git a/CHANGELOG.md b/CHANGELOG.md index f12c4957..ca75a26b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [4.0.1] - 2023-07-11 + +- Fixes duplicate users in users search queries when user is associated to multiple tenants + + ## [4.0.0] - 2023-06-02 ### Changes diff --git a/build.gradle b/build.gradle index 1609748d..09548caf 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "4.0.0" +version = "4.0.1" repositories { mavenCentral() 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 81d869d6..8b893162 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java @@ -223,7 +223,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) @@ -306,7 +306,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; @@ -352,7 +352,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 { @@ -371,13 +371,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("?"); @@ -389,9 +389,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<>(); @@ -400,7 +401,7 @@ public static List getUsersInfoUsingIdList(Start start, List i } return finalResult; }); - return userInfoWithTenantIds(start, userInfos); + return userInfoWithTenantIds(start, appIdentifier, userInfos); } return Collections.emptyList(); } @@ -423,7 +424,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, @@ -477,35 +478,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/storage/postgresql/queries/GeneralQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java index 05bc0d26..d200e011 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -712,6 +712,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 @@ -737,15 +738,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) { @@ -810,6 +810,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 @@ -985,7 +986,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 @@ -1004,16 +1005,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()); } @@ -1035,12 +1036,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("?"); @@ -1052,9 +1053,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/storage/postgresql/queries/PasswordlessQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java index 0d03bc81..067e5afe 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java @@ -417,7 +417,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) { @@ -664,13 +664,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) { @@ -681,9 +681,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, 1 is appId + pst.setString(i + 2, ids.get(i)); } }, result -> { List finalResult = new ArrayList<>(); @@ -692,7 +693,7 @@ public static List getUsersByIdList(Start start, List ids) } return finalResult; }); - return userInfoWithTenantIds(start, userInfos); + return userInfoWithTenantIds(start, appIdentifier, userInfos); } return Collections.emptyList(); } @@ -710,7 +711,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 { @@ -749,7 +750,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) @@ -771,7 +772,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) @@ -827,35 +828,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/storage/postgresql/queries/ThirdPartyQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java index dc56177f..d904b7b1 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java @@ -145,7 +145,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; @@ -192,19 +192,18 @@ 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("?"); if (i != ids.size() - 1) { // not the last element @@ -214,9 +213,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<>(); @@ -225,7 +225,7 @@ public static List getUsersInfoUsingIdList(Start start, List i } return finalResult; }); - return userInfoWithTenantIds(start, userInfos); + return userInfoWithTenantIds(start, appIdentifier, userInfos); } return Collections.emptyList(); } @@ -254,7 +254,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, @@ -289,7 +289,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, @@ -335,7 +335,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) @@ -390,35 +390,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,