Skip to content

Commit

Permalink
fix: users search queries (#744)
Browse files Browse the repository at this point in the history
* fix: user queries based on appid

* fix: dashboard search queries

* fix: app id in user id queries

* chore: changelog

* chore: version update
  • Loading branch information
sattvikc authored Jul 11, 2023
1 parent c7106a5 commit 227b924
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 72 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
// }
//}

version = "6.0.2"
version = "6.0.3"


repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -357,13 +355,13 @@ public static UserInfoPartial getUserInfoUsingId(Start start, Connection sqlCon,
});
}

public static List<UserInfo> getUsersInfoUsingIdList(Start start, List<String> ids)
public static List<UserInfo> getUsersInfoUsingIdList(Start start, AppIdentifier appIdentifier, List<String> 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("?");
Expand All @@ -375,9 +373,10 @@ public static List<UserInfo> getUsersInfoUsingIdList(Start start, List<String> i
QUERY.append(")");

List<UserInfoPartial> 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<UserInfoPartial> finalResult = new ArrayList<>();
Expand All @@ -386,7 +385,7 @@ public static List<UserInfo> getUsersInfoUsingIdList(Start start, List<String> i
}
return finalResult;
});
return userInfoWithTenantIds(start, userInfos);
return userInfoWithTenantIds(start, appIdentifier, userInfos);
}
return Collections.emptyList();
}
Expand All @@ -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,
Expand Down Expand Up @@ -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<UserInfo> userInfoWithTenantIds(Start start, List<UserInfoPartial> userInfos)
private static List<UserInfo> userInfoWithTenantIds(Start start, AppIdentifier appIdentifier, List<UserInfoPartial> 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<UserInfo> userInfoWithTenantIds_transaction(Start start, Connection sqlCon, List<UserInfoPartial> userInfos)
private static List<UserInfo> userInfoWithTenantIds_transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, List<UserInfoPartial> 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<String, List<String>> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, userIds);
Map<String, List<String>> tenantIdsForUserIds = GeneralQueries.getTenantIdsForUserIds_transaction(start, sqlCon, appIdentifier, userIds);
List<UserInfo> result = new ArrayList<>();
for (UserInfoPartial userInfo : userInfos) {
result.add(new UserInfo(userInfo.id, userInfo.email, userInfo.passwordHash, userInfo.timeJoined,
Expand Down
37 changes: 20 additions & 17 deletions src/main/java/io/supertokens/inmemorydb/queries/GeneralQueries.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<? extends AuthRecipeUserInfo> 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
Expand All @@ -824,16 +826,16 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant
}

private static List<? extends AuthRecipeUserInfo> getUserInfoForRecipeIdFromUserIds(Start start,
TenantIdentifier tenantIdentifier,
AppIdentifier appIdentifier,
RECIPE_ID recipeId,
List<String> 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());
}
Expand All @@ -858,12 +860,12 @@ public static String getRecipeIdForUser_Transaction(Start start, Connection sqlC
});
}

public static Map<String, List<String>> getTenantIdsForUserIds_transaction(Start start, Connection sqlCon, String[] userIds)
public static Map<String, List<String>> 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("?");
Expand All @@ -875,9 +877,10 @@ public static Map<String, List<String>> 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<String, List<String>> finalResult = new HashMap<>();
Expand Down
Loading

0 comments on commit 227b924

Please sign in to comment.