Skip to content

Commit

Permalink
Merge branch 'feat/account-linking' into al-test-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
sattvikc committed Aug 31, 2023
2 parents 98fa30c + 5e0fb6f commit 49c0360
Show file tree
Hide file tree
Showing 10 changed files with 793 additions and 93 deletions.
68 changes: 41 additions & 27 deletions src/main/java/io/supertokens/authRecipe/AuthRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ private static CanLinkAccountsResult canLinkAccountsHelper(TransactionConnection
assert (recipeUser.loginMethods.length == 1);
LoginMethod recipeUserIdLM = recipeUser.loginMethods[0];

Set<String> tenantIds = recipeUser.tenantIds;
Set<String> tenantIds = new HashSet<>();
tenantIds.addAll(recipeUser.tenantIds);
tenantIds.addAll(primaryUser.tenantIds);

// we loop through the union of both the user's tenantIds and check that the criteria for
Expand All @@ -267,9 +268,12 @@ private static CanLinkAccountsResult canLinkAccountsHelper(TransactionConnection

if (recipeUserIdLM.email != null) {
AuthRecipeUserInfo[] usersWithSameEmail = storage
.listPrimaryUsersByEmail_Transaction(tenantIdentifier, con,
.listPrimaryUsersByEmail_Transaction(appIdentifierWithStorage, con,
recipeUserIdLM.email);
for (AuthRecipeUserInfo user : usersWithSameEmail) {
if (!user.tenantIds.contains(tenantId)) {
continue;
}
if (user.isPrimaryUser && !user.getSupertokensUserId().equals(primaryUser.getSupertokensUserId())) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(user.getSupertokensUserId(),
"This user's email is already associated with another user ID");
Expand All @@ -279,9 +283,12 @@ private static CanLinkAccountsResult canLinkAccountsHelper(TransactionConnection

if (recipeUserIdLM.phoneNumber != null) {
AuthRecipeUserInfo[] usersWithSamePhoneNumber = storage
.listPrimaryUsersByPhoneNumber_Transaction(tenantIdentifier, con,
.listPrimaryUsersByPhoneNumber_Transaction(appIdentifierWithStorage, con,
recipeUserIdLM.phoneNumber);
for (AuthRecipeUserInfo user : usersWithSamePhoneNumber) {
if (!user.tenantIds.contains(tenantId)) {
continue;
}
if (user.isPrimaryUser && !user.getSupertokensUserId().equals(primaryUser.getSupertokensUserId())) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(user.getSupertokensUserId(),
"This user's phone number is already associated with another user" +
Expand All @@ -291,16 +298,22 @@ private static CanLinkAccountsResult canLinkAccountsHelper(TransactionConnection
}

if (recipeUserIdLM.thirdParty != null) {
AuthRecipeUserInfo userWithSameThirdParty = storage
.getPrimaryUsersByThirdPartyInfo_Transaction(tenantIdentifier, con,
AuthRecipeUserInfo[] usersWithSameThirdParty = storage
.listPrimaryUsersByThirdPartyInfo_Transaction(appIdentifierWithStorage, con,
recipeUserIdLM.thirdParty.id, recipeUserIdLM.thirdParty.userId);
if (userWithSameThirdParty != null && userWithSameThirdParty.isPrimaryUser &&
!userWithSameThirdParty.getSupertokensUserId().equals(primaryUser.getSupertokensUserId())) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(
userWithSameThirdParty.getSupertokensUserId(),
"This user's third party login is already associated with another" +
" user ID");
for (AuthRecipeUserInfo userWithSameThirdParty : usersWithSameThirdParty) {
if (!userWithSameThirdParty.tenantIds.contains(tenantId)) {
continue;
}
if (userWithSameThirdParty.isPrimaryUser &&
!userWithSameThirdParty.getSupertokensUserId().equals(primaryUser.getSupertokensUserId())) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(
userWithSameThirdParty.getSupertokensUserId(),
"This user's third party login is already associated with another" +
" user ID");
}
}

}
}

Expand Down Expand Up @@ -461,18 +474,14 @@ private static CreatePrimaryUserResult canCreatePrimaryUserHelper(TransactionCon
LoginMethod loginMethod = targetUser.loginMethods[0];

for (String tenantId : targetUser.tenantIds) {
TenantIdentifier tenantIdentifier = new TenantIdentifier(
appIdentifierWithStorage.getConnectionUriDomain(), appIdentifierWithStorage.getAppId(),
tenantId);
// we do not bother with getting the tenantIdentifierWithStorage here because
// we get the tenants from the user itself, and the user can only be shared across
// tenants of the same storage - therefore, the storage will be the same.

if (loginMethod.email != null) {
AuthRecipeUserInfo[] usersWithSameEmail = storage
.listPrimaryUsersByEmail_Transaction(tenantIdentifier, con,
.listPrimaryUsersByEmail_Transaction(appIdentifierWithStorage, con,
loginMethod.email);
for (AuthRecipeUserInfo user : usersWithSameEmail) {
if (!user.tenantIds.contains(tenantId)) {
continue;
}
if (user.isPrimaryUser) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(user.getSupertokensUserId(),
"This user's email is already associated with another user ID");
Expand All @@ -482,7 +491,7 @@ private static CreatePrimaryUserResult canCreatePrimaryUserHelper(TransactionCon

if (loginMethod.phoneNumber != null) {
AuthRecipeUserInfo[] usersWithSamePhoneNumber = storage
.listPrimaryUsersByPhoneNumber_Transaction(tenantIdentifier, con,
.listPrimaryUsersByPhoneNumber_Transaction(appIdentifierWithStorage, con,
loginMethod.phoneNumber);
for (AuthRecipeUserInfo user : usersWithSamePhoneNumber) {
if (user.isPrimaryUser) {
Expand All @@ -494,14 +503,19 @@ private static CreatePrimaryUserResult canCreatePrimaryUserHelper(TransactionCon
}

if (loginMethod.thirdParty != null) {
AuthRecipeUserInfo userWithSameThirdParty = storage
.getPrimaryUsersByThirdPartyInfo_Transaction(tenantIdentifier, con,
AuthRecipeUserInfo[] usersWithSameThirdParty = storage
.listPrimaryUsersByThirdPartyInfo_Transaction(appIdentifierWithStorage, con,
loginMethod.thirdParty.id, loginMethod.thirdParty.userId);
if (userWithSameThirdParty != null && userWithSameThirdParty.isPrimaryUser) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(
userWithSameThirdParty.getSupertokensUserId(),
"This user's third party login is already associated with another" +
" user ID");
for (AuthRecipeUserInfo userWithSameThirdParty : usersWithSameThirdParty) {
if (!userWithSameThirdParty.tenantIds.contains(tenantId)) {
continue;
}
if (userWithSameThirdParty.isPrimaryUser) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(
userWithSameThirdParty.getSupertokensUserId(),
"This user's third party login is already associated with another" +
" user ID");
}
}
}
}
Expand Down
13 changes: 4 additions & 9 deletions src/main/java/io/supertokens/emailpassword/EmailPassword.java
Original file line number Diff line number Diff line change
Expand Up @@ -603,20 +603,15 @@ public static void updateUsersEmailOrPassword(AppIdentifierWithStorage appIdenti
if (email != null) {
if (user.isPrimaryUser) {
for (String tenantId : user.tenantIds) {
// we do not bother with getting the tenantIdentifierWithStorage here because
// we get the tenants from the user itself, and the user can only be shared across
// tenants of the same storage - therefore, the storage will be the same.
TenantIdentifier tenantIdentifier = new TenantIdentifier(
appIdentifierWithStorage.getConnectionUriDomain(),
appIdentifierWithStorage.getAppId(),
tenantId);

AuthRecipeUserInfo[] existingUsersWithNewEmail =
authRecipeStorage.listPrimaryUsersByEmail_Transaction(
tenantIdentifier, transaction,
appIdentifierWithStorage, transaction,
email);

for (AuthRecipeUserInfo userWithSameEmail : existingUsersWithNewEmail) {
if (!userWithSameEmail.tenantIds.contains(tenantId)) {
continue;
}
if (userWithSameEmail.isPrimaryUser && !userWithSameEmail.getSupertokensUserId().equals(user.getSupertokensUserId())) {
throw new StorageTransactionLogicException(
new EmailChangeNotAllowedException());
Expand Down
72 changes: 38 additions & 34 deletions src/main/java/io/supertokens/inmemorydb/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException;
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.multitenancy.sqlStorage.MultitenancySQLStorage;
import io.supertokens.pluginInterface.passwordless.PasswordlessCode;
import io.supertokens.pluginInterface.passwordless.PasswordlessDevice;
import io.supertokens.pluginInterface.passwordless.exception.*;
Expand Down Expand Up @@ -102,7 +103,7 @@
public class Start
implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage,
JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage,
MultitenancyStorage, TOTPSQLStorage, ActiveUsersStorage, DashboardSQLStorage, AuthRecipeSQLStorage {
MultitenancyStorage, MultitenancySQLStorage, TOTPSQLStorage, ActiveUsersStorage, DashboardSQLStorage, AuthRecipeSQLStorage {

private static final Object appenderLock = new Object();
private static final String APP_ID_KEY_NAME = "app_id";
Expand Down Expand Up @@ -2245,9 +2246,9 @@ public TenantConfig[] getAllTenants() throws StorageQueryException {
}

@Override
public boolean addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId)
throws TenantOrAppNotFoundException, UnknownUserIdException, StorageQueryException,
DuplicateEmailException, DuplicateThirdPartyUserException, DuplicatePhoneNumberException {
public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection conn, String userId)
throws StorageQueryException, TenantOrAppNotFoundException, DuplicateEmailException,
DuplicateThirdPartyUserException, DuplicatePhoneNumberException, UnknownUserIdException {
try {
return this.startTransaction(con -> {
Connection sqlCon = (Connection) con.getConnection();
Expand Down Expand Up @@ -2758,44 +2759,47 @@ public AuthRecipeUserInfo getPrimaryUserById_Transaction(AppIdentifier appIdenti
}

@Override
public AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(TenantIdentifier tenantIdentifier,
public AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(AppIdentifier appIdentifier,
TransactionConnection con, String email)
throws StorageQueryException {
try {
Connection sqlCon = (Connection) con.getConnection();
return GeneralQueries.listPrimaryUsersByEmail_Transaction(this, sqlCon, tenantIdentifier, email);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
return null; // TODO
// try {
// Connection sqlCon = (Connection) con.getConnection();
// return GeneralQueries.listPrimaryUsersByEmail_Transaction(this, sqlCon, appIdentifier, email);
// } catch (SQLException e) {
// throw new StorageQueryException(e);
// }
}

@Override
public AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(TenantIdentifier tenantIdentifier,
public AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(AppIdentifier appIdentifier,
TransactionConnection con,
String phoneNumber)
throws StorageQueryException {
try {
Connection sqlCon = (Connection) con.getConnection();
return GeneralQueries.listPrimaryUsersByPhoneNumber_Transaction(this, sqlCon, tenantIdentifier,
phoneNumber);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public AuthRecipeUserInfo getPrimaryUsersByThirdPartyInfo_Transaction(TenantIdentifier tenantIdentifier,
TransactionConnection con,
String thirdPartyId,
String thirdPartyUserId)
throws StorageQueryException {
try {
Connection sqlCon = (Connection) con.getConnection();
return GeneralQueries.getPrimaryUsersByThirdPartyInfo_Transaction(this, sqlCon, tenantIdentifier,
thirdPartyId, thirdPartyUserId);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
return null; // TODO
// try {
// Connection sqlCon = (Connection) con.getConnection();
// return GeneralQueries.listPrimaryUsersByPhoneNumber_Transaction(this, sqlCon, tenantIdentifier,
// phoneNumber);
// } catch (SQLException e) {
// throw new StorageQueryException(e);
// }
}

@Override
public AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(AppIdentifier appIdentifier,
TransactionConnection con,
String thirdPartyId,
String thirdPartyUserId)
throws StorageQueryException {
return null; // TODO
// try {
// Connection sqlCon = (Connection) con.getConnection();
// return GeneralQueries.getPrimaryUsersByThirdPartyInfo_Transaction(this, sqlCon, tenantIdentifier,
// thirdPartyId, thirdPartyUserId);
// } catch (SQLException e) {
// throw new StorageQueryException(e);
// }
}

@Override
Expand Down
Loading

0 comments on commit 49c0360

Please sign in to comment.