Compare commits

...

13 Commits

Author SHA1 Message Date
Sattvik Chakravarthy 0ece28b8d8 fix: using results for can make primary and can link 2026-01-02 16:54:57 +05:30
Sattvik Chakravarthy 8dad1ff94d fix: indent 2025-12-30 12:14:14 +05:30
Sattvik Chakravarthy 8d8c5a2431 fix: email change 2025-12-24 18:21:44 +05:30
Sattvik Chakravarthy 3a0839c4c0 fix: relax timeouts 2025-12-22 12:44:35 +05:30
Sattvik Chakravarthy a550f8361e fix: review comments 2025-12-22 12:01:18 +05:30
Sattvik Chakravarthy 8cf05effa3 fix: tests 2025-12-19 12:08:32 +05:30
Sattvik Chakravarthy 1c4ea8e925 fix: test app data 2025-12-19 10:50:59 +05:30
Sattvik Chakravarthy 504092e7f0 fix: delete user and bug fixes 2025-12-18 21:23:28 +05:30
Sattvik Chakravarthy 63021553df fix: associate tenant 2025-12-12 18:31:38 +05:30
Sattvik Chakravarthy 201a2ec160 fix: link accounts 2025-12-12 15:47:50 +05:30
Sattvik Chakravarthy 30e06e23fa fix: can and create primary 2025-12-11 13:16:20 +05:30
Sattvik Chakravarthy 04805a3f64 fix: concurrent test 2025-11-27 15:23:24 +05:30
Sattvik Chakravarthy 4fa7b07f60 fix: tests 2025-11-27 13:04:23 +05:30
55 changed files with 490 additions and 814 deletions

View File

@ -16,9 +16,26 @@
package io.supertokens.authRecipe;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import io.supertokens.Main;
import io.supertokens.ResourceDistributor;
import io.supertokens.authRecipe.exception.*;
import io.supertokens.authRecipe.exception.BulkImportRecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;
import io.supertokens.bulkimport.BulkImportUserUtils;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
import io.supertokens.multitenancy.exception.BadPermissionException;
@ -26,7 +43,9 @@ import io.supertokens.pluginInterface.RECIPE_ID;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.StorageUtils;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.CanBecomePrimaryResult;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage;
import io.supertokens.pluginInterface.bulkimport.BulkImportUser;
import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportBatchInsertException;
@ -44,12 +63,6 @@ import io.supertokens.session.Session;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.useridmapping.UserIdType;
import io.supertokens.utils.Utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;
/*This files contains functions that are common for all auth recipes*/
@ -286,12 +299,40 @@ public class AuthRecipe {
tenantIds.addAll(recipeUser.tenantIds);
tenantIds.addAll(primaryUser.tenantIds);
checkIfLoginMethodCanBeLinkedOnTenant(con, appIdentifier, authRecipeStorage, tenantIds, recipeUser.loginMethods[0], primaryUser);
Set<String> emails = new HashSet<>();
Set<String> phoneNumbers = new HashSet<>();
Set<LoginMethod.ThirdParty> thirdParties = new HashSet<>();
for (LoginMethod currLoginMethod : primaryUser.loginMethods) {
checkIfLoginMethodCanBeLinkedOnTenant(con, appIdentifier, authRecipeStorage, tenantIds, currLoginMethod, primaryUser);
for (var lm : primaryUser.loginMethods) {
if (lm.email != null) {
emails.add(lm.email);
}
if (lm.phoneNumber != null) {
phoneNumbers.add(lm.phoneNumber);
}
if (lm.thirdParty != null) {
thirdParties.add(lm.thirdParty);
}
}
for (var lm : recipeUser.loginMethods) {
if (lm.email != null) {
emails.add(lm.email);
}
if (lm.phoneNumber != null) {
phoneNumbers.add(lm.phoneNumber);
}
if (lm.thirdParty != null) {
thirdParties.add(lm.thirdParty);
}
}
io.supertokens.pluginInterface.authRecipe.CanLinkAccountsResult canLinkResult =
authRecipeStorage.checkIfLoginMethodsCanBeLinked_Transaction(con, appIdentifier, tenantIds, emails,
phoneNumbers, thirdParties, primaryUser.getSupertokensUserId());
if (!canLinkResult.ok) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(
canLinkResult.primaryUserId, canLinkResult.message);
}
return new CanLinkAccountsResult(recipeUser.getSupertokensUserId(), primaryUser.getSupertokensUserId(), false);
}
@ -350,79 +391,6 @@ public class AuthRecipe {
return results;
}
private static void checkIfLoginMethodCanBeLinkedOnTenant(TransactionConnection con, AppIdentifier appIdentifier,
AuthRecipeSQLStorage authRecipeStorage,
Set<String> tenantIds, LoginMethod currLoginMethod,
AuthRecipeUserInfo primaryUser)
throws StorageQueryException, AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException {
// we loop through the union of both the user's tenantIds and check that the criteria for
// linking accounts is not violated in any of them. We do a union and not an intersection
// cause if we did an intersection, and that yields that account linking is allowed, it could
// result in one tenant having two primary users with the same email. For example:
// - tenant1 has u1 with email e, and u2 with email e, primary user (one is ep, one is tp)
// - tenant2 has u3 with email e, primary user (passwordless)
// now if we want to link u3 with u1, we have to deny it cause if we don't, it will result in
// u1 and u2 to be primary users with the same email in the same tenant. If we do an
// intersection, we will get an empty set, but if we do a union, we will get both the tenants and
// do the checks in both.
for (String tenantId : tenantIds) {
// we do not bother with getting the storage for each tenant 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 (currLoginMethod.email != null) {
AuthRecipeUserInfo[] usersWithSameEmail =
authRecipeStorage.listPrimaryUsersByEmail_Transaction(appIdentifier, con, currLoginMethod.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");
}
}
}
if (currLoginMethod.phoneNumber != null) {
AuthRecipeUserInfo[] usersWithSamePhoneNumber =
authRecipeStorage.listPrimaryUsersByPhoneNumber_Transaction(appIdentifier, con,
currLoginMethod.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" +
" ID");
}
}
}
if (currLoginMethod.thirdParty != null) {
AuthRecipeUserInfo[] usersWithSameThirdParty = authRecipeStorage
.listPrimaryUsersByThirdPartyInfo_Transaction(appIdentifier, con,
currLoginMethod.thirdParty.id, currLoginMethod.thirdParty.userId);
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");
}
}
}
}
}
private static void bulkCheckIfLoginMethodCanBeLinkedOnTenant(TransactionConnection con, AppIdentifier appIdentifier,
AuthRecipeSQLStorage authRecipeStorage,
Set<String> tenantIds, BulkImportUser.LoginMethod currLoginMethod,
@ -707,62 +675,14 @@ public class AuthRecipe {
// this means that the user has only one login method since it's not a primary user
// nor is it linked to a primary user
assert (targetUser.loginMethods.length == 1);
LoginMethod loginMethod = targetUser.loginMethods[0];
for (String tenantId : targetUser.tenantIds) {
if (loginMethod.email != null) {
AuthRecipeUserInfo[] usersWithSameEmail = authRecipeStorage
.listPrimaryUsersByEmail_Transaction(appIdentifier, 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");
}
}
}
CanBecomePrimaryResult result = authRecipeStorage.checkIfLoginMethodCanBecomePrimary_Transaction(appIdentifier,
con, targetUser.loginMethods[0]);
if (loginMethod.phoneNumber != null) {
AuthRecipeUserInfo[] usersWithSamePhoneNumber = authRecipeStorage
.listPrimaryUsersByPhoneNumber_Transaction(appIdentifier, con,
loginMethod.phoneNumber);
for (AuthRecipeUserInfo user : usersWithSamePhoneNumber) {
if (!user.tenantIds.contains(tenantId)) {
continue;
}
if (user.isPrimaryUser) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(
user.getSupertokensUserId(),
"This user's phone number is already associated with another user" +
" ID");
}
}
}
if (loginMethod.thirdParty != null) {
AuthRecipeUserInfo[] usersWithSameThirdParty = authRecipeStorage
.listPrimaryUsersByThirdPartyInfo_Transaction(appIdentifier, con,
loginMethod.thirdParty.id, loginMethod.thirdParty.userId);
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");
}
}
}
if (!result.ok) {
throw new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(result.primaryUserId, result.message);
}
return new CreatePrimaryUserResult(targetUser, false);
}
@ -945,7 +865,7 @@ public class AuthRecipe {
return authRecipeStorage.startTransaction(con -> {
try {
CreatePrimaryUserResult result = canCreatePrimaryUserHelper(con, appIdentifier, authRecipeStorage,
CreatePrimaryUserResult result = canCreatePrimaryUserHelper(con, appIdentifier, authRecipeStorage,
recipeUserId);
if (result.wasAlreadyAPrimaryUser) {
return result;
@ -1013,11 +933,11 @@ public class AuthRecipe {
}
canMakePrimaryUsers.add(result);
}
authRecipeStorage.makePrimaryUsers_Transaction(appIdentifier, con,
canMakePrimaryUsers.stream().map(canMakePrimaryUser -> canMakePrimaryUser.user.id).collect(
Collectors.toList()));
authRecipeStorage.makePrimaryUsers_Transaction(appIdentifier, con,
canMakePrimaryUsers.stream().map(canMakePrimaryUser -> canMakePrimaryUser.user.id).collect(
Collectors.toList()));
authRecipeStorage.commitTransaction(con);
authRecipeStorage.commitTransaction(con);
for(CreatePrimaryUserBulkResult result : results) {
if (result.wasAlreadyAPrimaryUser) {
@ -1447,6 +1367,8 @@ public class AuthRecipe {
.deleteThirdPartyUser_Transaction(con, appIdentifier, userId, deleteFromUserIdToAppIdTableToo);
StorageUtils.getPasswordlessStorage(storage)
.deletePasswordlessUser_Transaction(con, appIdentifier, userId, deleteFromUserIdToAppIdTableToo);
StorageUtils.getAuthRecipeStorage(storage)
.deleteAccountInfoReservations_Transaction(con, appIdentifier, userId);
}
public static boolean deleteNonAuthRecipeUser(TenantIdentifier tenantIdentifier, Storage storage, String userId)

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.supertokens.authRecipe.exception;
public class AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException extends Exception {
public final String primaryUserId;
public AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException(String primaryUserId, String description) {
super(description);
this.primaryUserId = primaryUserId;
}
}

View File

@ -20,7 +20,7 @@ import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.ResourceDistributor;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;
@ -29,9 +29,9 @@ import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.PasswordHashing;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.multitenancy.exception.AnotherPrimaryUserWithEmailAlreadyExistsException;
import io.supertokens.multitenancy.exception.AnotherPrimaryUserWithPhoneNumberAlreadyExistsException;
import io.supertokens.multitenancy.exception.AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithEmailAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithPhoneNumberAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException;
import io.supertokens.output.Logging;
import io.supertokens.passwordless.Passwordless;
import io.supertokens.pluginInterface.Storage;

View File

@ -24,6 +24,7 @@ import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException;
import org.jetbrains.annotations.TestOnly;
import io.supertokens.Main;
@ -31,7 +32,7 @@ import io.supertokens.ResourceDistributor;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.config.Config;
import io.supertokens.config.CoreConfig;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.emailpassword.exceptions.ResetPasswordInvalidTokenException;
import io.supertokens.emailpassword.exceptions.UnsupportedPasswordHashingFormatException;
import io.supertokens.emailpassword.exceptions.WrongCredentialsException;
@ -667,30 +668,10 @@ public class EmailPassword {
}
if (email != null) {
if (user.isPrimaryUser) {
for (String tenantId : user.tenantIds) {
AuthRecipeUserInfo[] existingUsersWithNewEmail =
authRecipeStorage.listPrimaryUsersByEmail_Transaction(
appIdentifier, 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());
}
}
}
}
try {
epStorage.updateUsersEmail_Transaction(appIdentifier, transaction,
userId, email);
} catch (DuplicateEmailException e) {
epStorage.updateUsersEmail_Transaction(appIdentifier, transaction, userId, email);
} catch (DuplicateEmailException | EmailChangeNotAllowedException |
PhoneNumberChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
}

View File

@ -1,21 +0,0 @@
/*
* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.supertokens.emailpassword.exceptions;
public class EmailChangeNotAllowedException extends Exception {
private static final long serialVersionUID = -7205953190075543040L;
}

View File

@ -24,6 +24,8 @@ import io.supertokens.inmemorydb.config.SQLiteConfig;
import io.supertokens.inmemorydb.queries.*;
import io.supertokens.pluginInterface.*;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.CanBecomePrimaryResult;
import io.supertokens.pluginInterface.authRecipe.CanLinkAccountsResult;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage;
import io.supertokens.pluginInterface.bulkimport.BulkImportStorage;
@ -1189,7 +1191,7 @@ public class Start
}
@Override
public void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con,
public void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId,
String thirdPartyId, String thirdPartyUserId,
String newEmail) throws StorageQueryException {
Connection sqlCon = (Connection) con.getConnection();
@ -1328,6 +1330,38 @@ public class Start
}
}
@Override
public CanBecomePrimaryResult checkIfLoginMethodCanBecomePrimary_Transaction(AppIdentifier appIdentifier, TransactionConnection con,
LoginMethod loginMethod) throws
StorageQueryException {
// TODO
return null;
}
@Override
public CanLinkAccountsResult checkIfLoginMethodsCanBeLinked_Transaction(TransactionConnection con,
AppIdentifier appIdentifier,
Set<String> tenantIds, Set<String> emails,
Set<String> phoneNumbers,
Set<LoginMethod.ThirdParty> thirdParties,
String primaryUserId)
throws StorageQueryException {
// TODO
return null;
}
@Override
public void addTenantIdToPrimaryUser_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con,
String supertokensUserId) {
// TODO
}
@Override
public void deleteAccountInfoReservations_Transaction(TransactionConnection con, AppIdentifier appIdentifier,
String userId) throws StorageQueryException {
// TODO
}
@Override
public void updateLastActive(AppIdentifier appIdentifier, String userId) throws StorageQueryException {
try {
@ -3648,7 +3682,7 @@ public class Start
public AuthRecipeUserInfo signUpWithCredentialsRegister_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con,
String userId, String email, String relyingPartyId, WebAuthNStoredCredential credential)
throws StorageQueryException, io.supertokens.pluginInterface.webauthn.exceptions.DuplicateUserIdException, TenantOrAppNotFoundException,
DuplicateUserEmailException {
DuplicateEmailException {
Connection sqlCon = (Connection) con.getConnection();
try {
return WebAuthNQueries.signUpWithCredentialRegister_Transaction(this, sqlCon, tenantIdentifier, userId, email, relyingPartyId, credential);
@ -3659,7 +3693,7 @@ public class Start
if (isUniqueConstraintError(serverMessage, config.getWebAuthNUserToTenantTable(),
new String[]{"app_id", "tenant_id", "email"})) {
throw new DuplicateUserEmailException();
throw new DuplicateEmailException();
} else if (isPrimaryKeyError(serverMessage, config.getWebAuthNUsersTable(),
new String[]{"app_id", "user_id"})
|| isPrimaryKeyError(serverMessage, config.getUsersTable(),
@ -3693,7 +3727,7 @@ public class Start
@Override
public AuthRecipeUserInfo signUp_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con,
String userId, String email, String relyingPartyId)
throws StorageQueryException, TenantOrAppNotFoundException, DuplicateUserEmailException,
throws StorageQueryException, TenantOrAppNotFoundException, DuplicateEmailException,
io.supertokens.pluginInterface.webauthn.exceptions.DuplicateUserIdException {
Connection sqlCon = (Connection) con.getConnection();
try {
@ -3705,7 +3739,7 @@ public class Start
if (isUniqueConstraintError(serverMessage, config.getWebAuthNUserToTenantTable(),
new String[]{"app_id", "tenant_id", "email"})) {
throw new DuplicateUserEmailException();
throw new DuplicateEmailException();
} else if (isPrimaryKeyError(serverMessage, config.getWebAuthNUsersTable(),
new String[]{"app_id", "user_id"})
|| isPrimaryKeyError(serverMessage, config.getUsersTable(),
@ -3809,7 +3843,7 @@ public class Start
@Override
public void updateUserEmail(TenantIdentifier tenantIdentifier, String userId, String newEmail)
throws StorageQueryException, io.supertokens.pluginInterface.webauthn.exceptions.UserIdNotFoundException,
DuplicateUserEmailException {
DuplicateEmailException {
try {
WebAuthNQueries.updateUserEmail(this, tenantIdentifier, userId, newEmail);
} catch (StorageQueryException e) {
@ -3819,7 +3853,7 @@ public class Start
if (isUniqueConstraintError(errorMessage, config.getWebAuthNUserToTenantTable(),
new String[]{"app_id", "tenant_id", "email"})) {
throw new DuplicateUserEmailException();
throw new DuplicateEmailException();
} else if (isForeignKeyConstraintError(
errorMessage,
config.getWebAuthNUserToTenantTable(),
@ -3836,7 +3870,7 @@ public class Start
public void updateUserEmail_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId,
String newEmail)
throws StorageQueryException, io.supertokens.pluginInterface.webauthn.exceptions.UserIdNotFoundException,
DuplicateUserEmailException {
DuplicateEmailException {
try {
Connection sqlCon = (Connection) con.getConnection();
WebAuthNQueries.updateUserEmail_Transaction(this, sqlCon, tenantIdentifier, userId, newEmail);
@ -3847,7 +3881,7 @@ public class Start
if (isUniqueConstraintError(errorMessage, config.getWebAuthNUserToTenantTable(),
new String[]{"app_id", "tenant_id", "email"})) {
throw new DuplicateUserEmailException();
throw new DuplicateEmailException();
} else if (isForeignKeyConstraintError(
errorMessage,
config.getWebAuthNUserToTenantTable(),

View File

@ -16,8 +16,19 @@
package io.supertokens.multitenancy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.TestOnly;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.ResourceDistributor;
import io.supertokens.authRecipe.AuthRecipe;
@ -26,13 +37,19 @@ import io.supertokens.config.CoreConfig;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlag;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
import io.supertokens.multitenancy.exception.*;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithEmailAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithPhoneNumberAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.multitenancy.exception.CannotDeleteNullAppIdException;
import io.supertokens.multitenancy.exception.CannotDeleteNullConnectionUriDomainException;
import io.supertokens.multitenancy.exception.CannotDeleteNullTenantException;
import io.supertokens.multitenancy.exception.CannotModifyBaseConfigException;
import io.supertokens.pluginInterface.KeyValueInfo;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.StorageUtils;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException;
@ -53,10 +70,6 @@ import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUs
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.thirdparty.InvalidProviderConfigException;
import io.supertokens.thirdparty.ThirdParty;
import org.jetbrains.annotations.TestOnly;
import java.io.IOException;
import java.util.*;
public class Multitenancy extends ResourceDistributor.SingletonResource {
@ -412,123 +425,23 @@ public class Multitenancy extends ResourceDistributor.SingletonResource {
AuthRecipeUserInfo userToAssociate = authRecipeStorage.getPrimaryUserById_Transaction(
tenantIdentifier.toAppIdentifier(), con, userId);
if (userToAssociate != null && userToAssociate.isPrimaryUser) {
Set<String> emails = new HashSet<>();
Set<String> phoneNumbers = new HashSet<>();
Set<LoginMethod.ThirdParty> thirdParties = new HashSet<>();
// Loop through all the emails, phoneNumbers and thirdPartyInfos and check for conflicts
for (LoginMethod lM : userToAssociate.loginMethods) {
if (lM.email != null) {
emails.add(lM.email);
}
if (lM.phoneNumber != null) {
phoneNumbers.add(lM.phoneNumber);
}
if (lM.thirdParty != null) {
thirdParties.add(lM.thirdParty);
}
}
for (String email : emails) {
AuthRecipeUserInfo[] usersWithSameEmail = authRecipeStorage.listPrimaryUsersByEmail_Transaction(
tenantIdentifier.toAppIdentifier(), con, email);
for (AuthRecipeUserInfo userWithSameEmail : usersWithSameEmail) {
if (userWithSameEmail.getSupertokensUserId()
.equals(userToAssociate.getSupertokensUserId())) {
continue; // it's the same user, no need to check anything
}
if (userWithSameEmail.isPrimaryUser && userWithSameEmail.tenantIds.contains(tenantId) &&
!userWithSameEmail.getSupertokensUserId().equals(userId)) {
for (LoginMethod lm1 : userWithSameEmail.loginMethods) {
if (lm1.tenantIds.contains(tenantId)) {
for (LoginMethod lm2 : userToAssociate.loginMethods) {
if (lm1.recipeId.equals(lm2.recipeId) && email.equals(lm1.email) &&
lm1.email.equals(lm2.email)) {
throw new StorageTransactionLogicException(
new DuplicateEmailException());
}
}
}
}
throw new StorageTransactionLogicException(
new AnotherPrimaryUserWithEmailAlreadyExistsException(
userWithSameEmail.getSupertokensUserId()));
}
}
}
for (String phoneNumber : phoneNumbers) {
AuthRecipeUserInfo[] usersWithSamePhoneNumber =
authRecipeStorage.listPrimaryUsersByPhoneNumber_Transaction(
tenantIdentifier.toAppIdentifier(), con, phoneNumber);
for (AuthRecipeUserInfo userWithSamePhoneNumber : usersWithSamePhoneNumber) {
if (userWithSamePhoneNumber.getSupertokensUserId()
.equals(userToAssociate.getSupertokensUserId())) {
continue; // it's the same user, no need to check anything
}
if (userWithSamePhoneNumber.tenantIds.contains(tenantId) &&
!userWithSamePhoneNumber.getSupertokensUserId().equals(userId)) {
for (LoginMethod lm1 : userWithSamePhoneNumber.loginMethods) {
if (lm1.tenantIds.contains(tenantId)) {
for (LoginMethod lm2 : userToAssociate.loginMethods) {
if (lm1.recipeId.equals(lm2.recipeId) &&
phoneNumber.equals(lm1.phoneNumber) &&
lm1.phoneNumber.equals(lm2.phoneNumber)) {
throw new StorageTransactionLogicException(
new DuplicatePhoneNumberException());
}
}
}
}
throw new StorageTransactionLogicException(
new AnotherPrimaryUserWithPhoneNumberAlreadyExistsException(
userWithSamePhoneNumber.getSupertokensUserId()));
}
}
}
for (LoginMethod.ThirdParty tp : thirdParties) {
AuthRecipeUserInfo[] usersWithSameThirdPartyInfo =
authRecipeStorage.listPrimaryUsersByThirdPartyInfo_Transaction(
tenantIdentifier.toAppIdentifier(), con, tp.id, tp.userId);
for (AuthRecipeUserInfo userWithSameThirdPartyInfo : usersWithSameThirdPartyInfo) {
if (userWithSameThirdPartyInfo.getSupertokensUserId()
.equals(userToAssociate.getSupertokensUserId())) {
continue; // it's the same user, no need to check anything
}
if (userWithSameThirdPartyInfo.tenantIds.contains(tenantId) &&
!userWithSameThirdPartyInfo.getSupertokensUserId().equals(userId)) {
for (LoginMethod lm1 : userWithSameThirdPartyInfo.loginMethods) {
if (lm1.tenantIds.contains(tenantId)) {
for (LoginMethod lm2 : userToAssociate.loginMethods) {
if (lm1.recipeId.equals(lm2.recipeId) && tp.equals(lm1.thirdParty) &&
lm1.thirdParty.equals(lm2.thirdParty)) {
throw new StorageTransactionLogicException(
new DuplicateThirdPartyUserException());
}
}
}
}
throw new StorageTransactionLogicException(
new AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException(
userWithSameThirdPartyInfo.getSupertokensUserId()));
}
}
}
}
// userToAssociate may be null if the user is not associated to any tenants, we can still try and
// associate it. This happens only in CDI 3.0 where we allow disassociation from all tenants
// This will not happen in CDI >= 4.0 because we will not allow disassociation from all tenants
try {
if (userToAssociate != null && userToAssociate.isPrimaryUser) {
authRecipeStorage.addTenantIdToPrimaryUser_Transaction(tenantIdentifier, con, userToAssociate.getSupertokensUserId());
}
// userToAssociate may be null if the user is not associated to any tenants, we can still try and
// associate it. This happens only in CDI 3.0 where we allow disassociation from all tenants
// This will not happen in CDI >= 4.0 because we will not allow disassociation from all tenants
boolean result = ((MultitenancySQLStorage) storage).addUserIdToTenant_Transaction(tenantIdentifier,
con, userId);
authRecipeStorage.commitTransaction(con);
return result;
} catch (TenantOrAppNotFoundException | UnknownUserIdException | DuplicatePhoneNumberException |
DuplicateThirdPartyUserException | DuplicateEmailException e) {
DuplicateThirdPartyUserException | DuplicateEmailException |
AnotherPrimaryUserWithPhoneNumberAlreadyExistsException |
AnotherPrimaryUserWithEmailAlreadyExistsException |
AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException e) {
throw new StorageTransactionLogicException(e);
}
});

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.supertokens.multitenancy.exception;
public class AnotherPrimaryUserWithEmailAlreadyExistsException extends Exception {
public AnotherPrimaryUserWithEmailAlreadyExistsException(String primaryUserId) {
super("Another primary user with email already exists: " + primaryUserId);
}
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.supertokens.multitenancy.exception;
public class AnotherPrimaryUserWithPhoneNumberAlreadyExistsException extends Exception {
public AnotherPrimaryUserWithPhoneNumberAlreadyExistsException(String primaryUserId) {
super("Another primary user with phone number already exists: " + primaryUserId);
}
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.supertokens.multitenancy.exception;
public class AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException extends Exception {
public AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException(String primaryUserId) {
super("Another primary user with third party info already exists: " + primaryUserId);
}
}

View File

@ -20,7 +20,7 @@ import io.supertokens.Main;
import io.supertokens.ResourceDistributor;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.config.Config;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.passwordless.exceptions.*;
@ -739,7 +739,7 @@ public class Passwordless {
FieldUpdate emailUpdate, FieldUpdate phoneNumberUpdate)
throws StorageQueryException, UnknownUserIdException, DuplicateEmailException,
DuplicatePhoneNumberException, UserWithoutContactInfoException, EmailChangeNotAllowedException,
PhoneNumberChangeNotAllowedException {
io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException {
Storage storage = StorageLayer.getStorage(main);
updateUser(ResourceDistributor.getAppForTesting().toAppIdentifier(), storage,
userId, emailUpdate, phoneNumberUpdate);
@ -749,7 +749,7 @@ public class Passwordless {
FieldUpdate emailUpdate, FieldUpdate phoneNumberUpdate)
throws StorageQueryException, UnknownUserIdException, DuplicateEmailException,
DuplicatePhoneNumberException, UserWithoutContactInfoException, EmailChangeNotAllowedException,
PhoneNumberChangeNotAllowedException {
io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException {
PasswordlessSQLStorage plStorage = StorageUtils.getPasswordlessStorage(storage);
// We do not lock the user here, because we decided that even if the device cleanup used outdated information
@ -778,29 +778,10 @@ public class Passwordless {
AuthRecipeSQLStorage authRecipeSQLStorage = StorageUtils.getAuthRecipeStorage(storage);
plStorage.startTransaction(con -> {
if (emailUpdate != null && !Objects.equals(emailUpdate.newValue, lM.email)) {
if (user.isPrimaryUser) {
for (String tenantId : user.tenantIds) {
AuthRecipeUserInfo[] existingUsersWithNewEmail =
authRecipeSQLStorage.listPrimaryUsersByEmail_Transaction(
appIdentifier, con,
emailUpdate.newValue);
for (AuthRecipeUserInfo userWithSameEmail : existingUsersWithNewEmail) {
if (!userWithSameEmail.tenantIds.contains(tenantId)) {
continue;
}
if (userWithSameEmail.isPrimaryUser &&
!userWithSameEmail.getSupertokensUserId().equals(user.getSupertokensUserId())) {
throw new StorageTransactionLogicException(
new EmailChangeNotAllowedException());
}
}
}
}
try {
plStorage.updateUserEmail_Transaction(appIdentifier, con, recipeUserId,
emailUpdate.newValue);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
if (lM.email != null) {
@ -828,7 +809,7 @@ public class Passwordless {
!userWithSamePhoneNumber.getSupertokensUserId()
.equals(user.getSupertokensUserId())) {
throw new StorageTransactionLogicException(
new PhoneNumberChangeNotAllowedException());
new io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException());
}
}
}
@ -868,8 +849,8 @@ public class Passwordless {
throw (EmailChangeNotAllowedException) e.actualException;
}
if (e.actualException instanceof PhoneNumberChangeNotAllowedException) {
throw (PhoneNumberChangeNotAllowedException) e.actualException;
if (e.actualException instanceof io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException) {
throw (io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException) e.actualException;
}
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.supertokens.passwordless.exceptions;
public class PhoneNumberChangeNotAllowedException extends Exception {
}

View File

@ -18,7 +18,7 @@ package io.supertokens.thirdparty;
import io.supertokens.Main;
import io.supertokens.ResourceDistributor;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.pluginInterface.RECIPE_ID;
@ -27,6 +27,7 @@ import io.supertokens.pluginInterface.StorageUtils;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
@ -296,29 +297,12 @@ public class ThirdParty {
}
if (!email.equals(lM1.email)) {
// before updating the email, we must check for if another primary user has the same
// email, and if they do, then we do not allow the update.
if (userFromDb1.isPrimaryUser) {
for (String tenantId : userFromDb1.tenantIds) {
AuthRecipeUserInfo[] userBasedOnEmail =
authRecipeStorage.listPrimaryUsersByEmail_Transaction(
appIdentifier, con, email
);
for (AuthRecipeUserInfo userWithSameEmail : userBasedOnEmail) {
if (!userWithSameEmail.tenantIds.contains(tenantId)) {
continue;
}
if (userWithSameEmail.isPrimaryUser &&
!userWithSameEmail.getSupertokensUserId()
.equals(userFromDb1.getSupertokensUserId())) {
throw new StorageTransactionLogicException(
new EmailChangeNotAllowedException());
}
}
}
try {
tpStorage.updateUserEmail_Transaction(appIdentifier, con, lM1.getSupertokensUserId(),
thirdPartyId, thirdPartyUserId, email);
} catch (EmailChangeNotAllowedException | DuplicateEmailException e) {
throw new StorageTransactionLogicException(e);
}
tpStorage.updateUserEmail_Transaction(appIdentifier, con,
thirdPartyId, thirdPartyUserId, email);
}
tpStorage.commitTransaction(con);

View File

@ -37,7 +37,9 @@ import io.supertokens.pluginInterface.StorageUtils;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
@ -281,7 +283,7 @@ public class WebAuthN {
public static WebAuthNSignInUpResult signUp(Storage storage, TenantIdentifier tenantIdentifier,
String optionsId, JsonObject credentialDataJson)
throws InvalidWebauthNOptionsException, DuplicateUserEmailException, WebauthNVerificationFailedException,
throws InvalidWebauthNOptionsException, DuplicateEmailException, WebauthNVerificationFailedException,
StorageQueryException, WebauthNOptionsNotExistsException, WebauthNInvalidFormatException{
// create a new user in the auth recipe storage
// create new credentials
@ -319,7 +321,7 @@ public class WebAuthN {
} catch (DuplicateUserIdException duplicateUserIdException) {
//ignore and retry
} catch (InvalidWebauthNOptionsException | TenantOrAppNotFoundException |
DuplicateUserEmailException | WebauthNVerificationFailedException |
DuplicateEmailException | WebauthNVerificationFailedException |
WebauthNInvalidFormatException | WebauthNOptionsNotExistsException e) {
throw new StorageQueryException(e);
}
@ -329,8 +331,8 @@ public class WebAuthN {
} catch (StorageQueryException exception) {
if (exception.getCause() instanceof InvalidWebauthNOptionsException) {
throw (InvalidWebauthNOptionsException) exception.getCause();
} else if (exception.getCause() instanceof DuplicateUserEmailException) {
throw (DuplicateUserEmailException) exception.getCause();
} else if (exception.getCause() instanceof DuplicateEmailException) {
throw (DuplicateEmailException) exception.getCause();
} else if (exception.getCause() instanceof WebauthNVerificationFailedException) {
throw (WebauthNVerificationFailedException) exception.getCause();
} else if (exception.getCause() instanceof WebauthNOptionsNotExistsException) {
@ -355,22 +357,22 @@ public class WebAuthN {
@TestOnly
public static AuthRecipeUserInfo saveUser(Storage storage, TenantIdentifier tenantIdentifier, String email, String userId, String rpId)
throws StorageQueryException, TenantOrAppNotFoundException, DuplicateUserEmailException,
throws StorageQueryException, TenantOrAppNotFoundException, DuplicateEmailException,
DuplicateUserIdException {
WebAuthNSQLStorage webAuthNStorage = StorageUtils.getWebAuthNStorage(storage);
try {
return webAuthNStorage.startTransaction(con -> {
try {
return webAuthNStorage.signUp_Transaction(tenantIdentifier, con, userId, email, rpId);
} catch (TenantOrAppNotFoundException | DuplicateUserEmailException | DuplicateUserIdException e) {
} catch (TenantOrAppNotFoundException | DuplicateEmailException | DuplicateUserIdException e) {
throw new StorageTransactionLogicException(e);
}
});
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw (TenantOrAppNotFoundException) e.actualException;
} else if (e.actualException instanceof DuplicateUserEmailException) {
throw (DuplicateUserEmailException) e.actualException;
} else if (e.actualException instanceof DuplicateEmailException) {
throw (DuplicateEmailException) e.actualException;
} else if (e.actualException instanceof DuplicateUserIdException) {
throw (DuplicateUserIdException) e.actualException;
} else {
@ -633,7 +635,7 @@ public class WebAuthN {
}
public static void updateUserEmail(Storage storage, TenantIdentifier tenantIdentifier, String userId, String email)
throws StorageQueryException, UserIdNotFoundException, DuplicateUserEmailException,
throws StorageQueryException, UserIdNotFoundException, DuplicateEmailException,
EmailChangeNotAllowedException, StorageTransactionLogicException, TenantOrAppNotFoundException {
WebAuthNSQLStorage webAuthNStorage = StorageUtils.getWebAuthNStorage(storage);
AuthRecipeSQLStorage authRecipeStorage = StorageUtils.getAuthRecipeStorage(storage);
@ -659,30 +661,10 @@ public class WebAuthN {
}
if (email != null) {
if (user.isPrimaryUser) {
for (String tenantId : user.tenantIds) {
AuthRecipeUserInfo[] existingUsersWithNewEmail =
authRecipeStorage.listPrimaryUsersByEmail_Transaction(
tenantIdentifier.toAppIdentifier(), con,
email);
for (AuthRecipeUserInfo userWithSameEmail : existingUsersWithNewEmail) {
if (!userWithSameEmail.tenantIds.contains(tenantId)) {
continue;
}
if (userWithSameEmail.isPrimaryUser && !userWithSameEmail.getSupertokensUserId()
.equals(user.getSupertokensUserId())) {
throw new StorageTransactionLogicException(
new EmailChangeNotAllowedException());
}
}
}
}
try {
webAuthNStorage.updateUserEmail_Transaction(tenantIdentifier, con,
userIdFromMapping, email);
} catch (UserIdNotFoundException | DuplicateUserEmailException e) {
} catch (UserIdNotFoundException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
}
@ -692,8 +674,8 @@ public class WebAuthN {
Throwable cause = e.getCause();
if (cause instanceof UserIdNotFoundException){
throw (UserIdNotFoundException) cause;
} else if (cause instanceof DuplicateUserEmailException) {
throw (DuplicateUserEmailException) cause;
} else if (cause instanceof DuplicateEmailException) {
throw (DuplicateEmailException) cause;
} else if (cause instanceof EmailChangeNotAllowedException) {
throw (EmailChangeNotAllowedException) cause;
} else if (cause instanceof TenantOrAppNotFoundException) {

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.supertokens.webauthn.exception;
public class EmailChangeNotAllowedException extends Exception {
public EmailChangeNotAllowedException() {
super();
}
}

View File

@ -20,7 +20,7 @@ import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.StorageAndUserIdMapping;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.pluginInterface.RECIPE_ID;

View File

@ -20,7 +20,7 @@ import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.StorageAndUserIdMapping;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.multitenancy.exception.BadPermissionException;

View File

@ -20,7 +20,7 @@ import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.StorageAndUserIdMapping;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
import io.supertokens.multitenancy.exception.BadPermissionException;

View File

@ -21,7 +21,7 @@ import io.supertokens.ActiveUsers;
import io.supertokens.Main;
import io.supertokens.StorageAndUserIdMapping;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;

View File

@ -20,7 +20,7 @@ import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.StorageAndUserIdMapping;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.output.Logging;
import io.supertokens.pluginInterface.RECIPE_ID;

View File

@ -21,9 +21,9 @@ import io.supertokens.Main;
import io.supertokens.StorageAndUserIdMapping;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.multitenancy.exception.AnotherPrimaryUserWithEmailAlreadyExistsException;
import io.supertokens.multitenancy.exception.AnotherPrimaryUserWithPhoneNumberAlreadyExistsException;
import io.supertokens.multitenancy.exception.AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithEmailAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithPhoneNumberAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException;
import io.supertokens.pluginInterface.RECIPE_ID;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException;

View File

@ -19,11 +19,11 @@ package io.supertokens.webserver.api.passwordless;
import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.StorageAndUserIdMapping;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.passwordless.Passwordless;
import io.supertokens.passwordless.Passwordless.FieldUpdate;
import io.supertokens.passwordless.exceptions.PhoneNumberChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException;
import io.supertokens.passwordless.exceptions.UserWithoutContactInfoException;
import io.supertokens.pluginInterface.RECIPE_ID;
import io.supertokens.pluginInterface.Storage;

View File

@ -19,7 +19,7 @@ package io.supertokens.webserver.api.thirdparty;
import com.google.gson.JsonObject;
import io.supertokens.ActiveUsers;
import io.supertokens.Main;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.pluginInterface.RECIPE_ID;
import io.supertokens.pluginInterface.Storage;

View File

@ -20,10 +20,10 @@ import com.google.gson.JsonObject;
import io.supertokens.ActiveUsers;
import io.supertokens.Main;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.webauthn.exceptions.DuplicateUserEmailException;
import io.supertokens.pluginInterface.webauthn.exceptions.WebauthNOptionsNotExistsException;
import io.supertokens.utils.SemVer;
import io.supertokens.webauthn.WebAuthN;
@ -86,7 +86,7 @@ public class SignUpWithCredentialRegisterAPI extends WebserverAPI {
result.addProperty("status", "INVALID_OPTIONS_ERROR");
result.addProperty("reason", e.getMessage());
sendJsonResponse(200, result, resp);
} catch (DuplicateUserEmailException e) {
} catch (DuplicateEmailException e) {
JsonObject result = new JsonObject();
result.addProperty("status", "EMAIL_ALREADY_EXISTS_ERROR");
sendJsonResponse(200, result, resp);

View File

@ -19,14 +19,14 @@ package io.supertokens.webserver.api.webauthn;
import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.webauthn.exceptions.DuplicateUserEmailException;
import io.supertokens.pluginInterface.webauthn.exceptions.UserIdNotFoundException;
import io.supertokens.webauthn.WebAuthN;
import io.supertokens.webauthn.exception.EmailChangeNotAllowedException;
import io.supertokens.webserver.InputParser;
import io.supertokens.webserver.WebserverAPI;
import jakarta.servlet.ServletException;
@ -68,7 +68,7 @@ public class UpdateUserEmailAPI extends WebserverAPI {
JsonObject result = new JsonObject();
result.addProperty("status", "UNKNOWN_USER_ID_ERROR");
sendJsonResponse(200, result, resp);
} catch (DuplicateUserEmailException e) {
} catch (DuplicateEmailException e) {
JsonObject result = new JsonObject();
result.addProperty("status", "EMAIL_ALREADY_EXISTS_ERROR");
sendJsonResponse(200, result, resp);

View File

@ -22,7 +22,7 @@ import io.supertokens.ProcessState;
import io.supertokens.ResourceDistributor;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.UserPaginationContainer;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;

View File

@ -18,7 +18,7 @@ package io.supertokens.test;
import io.supertokens.ProcessState;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.emailpassword.exceptions.WrongCredentialsException;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;

View File

@ -69,9 +69,6 @@ public class StorageTest {
Utils.reset();
}
@Rule
public Retry retry = new Retry(3);
@Test
public void transactionIsolationWithoutAnInitialRowTesting() throws Exception {
String[] args = {"../"};
@ -144,10 +141,9 @@ public class StorageTest {
KeyValueInfo info = sqlStorage.getKeyValue_Transaction(
new TenantIdentifier(null, null, null), con, key);
if (numberOfIterations.get() != 1) {
assert (info == null);
} else {
assert (info != null);
if (info != null) {
assertTrue(numberOfIterations.get() > 0);
}
try {
@ -184,7 +180,7 @@ public class StorageTest {
t2.join();
assertEquals(endValueOfCon1.get(), endValueOfCon2.get());
assertEquals(numberOfIterations.get(), 1);
assertTrue(numberOfIterations.get() >= 1);
}

View File

@ -19,7 +19,7 @@ package io.supertokens.test.accountlinking;
import com.google.gson.JsonObject;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.featureflag.EE_FEATURES;

View File

@ -21,7 +21,7 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;
@ -34,7 +34,6 @@ import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.passwordless.exception.DuplicateLinkCodeHashException;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.test.TestingProcessManager;

View File

@ -20,7 +20,7 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;

View File

@ -20,7 +20,7 @@ import com.google.gson.JsonObject;
import io.supertokens.ActiveUsers;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.emailpassword.EmailPassword;

View File

@ -16,58 +16,68 @@
package io.supertokens.test.accountlinking;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.emailpassword.exceptions.WrongCredentialsException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.multitenancy.exception.*;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.multitenancy.exception.CannotModifyBaseConfigException;
import io.supertokens.passwordless.Passwordless;
import io.supertokens.passwordless.exceptions.PhoneNumberChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.PhoneNumberChangeNotAllowedException;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithEmailAlreadyExistsException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithPhoneNumberAlreadyExistsException;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException;
import io.supertokens.pluginInterface.exceptions.InvalidConfigException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.*;
import io.supertokens.pluginInterface.multitenancy.EmailPasswordConfig;
import io.supertokens.pluginInterface.multitenancy.PasswordlessConfig;
import io.supertokens.pluginInterface.multitenancy.TenantConfig;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.ThirdPartyConfig;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.passwordless.exception.DuplicatePhoneNumberException;
import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.test.TestingProcessManager;
import io.supertokens.test.Utils;
import io.supertokens.thirdparty.InvalidProviderConfigException;
import io.supertokens.thirdparty.ThirdParty;
import io.supertokens.userroles.UserRoles;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
public class MultitenantTest {
@Rule
public TestRule watchman = Utils.getOnFailure();
@Rule
public TestRule retryFlaky = Utils.retryFlakyTest();
// @Rule
// public TestRule retryFlaky = Utils.retryFlakyTest();
@AfterClass
public static void afterTesting() {
@ -79,7 +89,7 @@ public class MultitenantTest {
Utils.reset();
}
TenantIdentifier t1, t2, t3, t4;
TenantIdentifier t1, t2, t3;
private void createTenants(Main main)
throws StorageQueryException, TenantOrAppNotFoundException, InvalidProviderConfigException,
@ -375,22 +385,21 @@ public class MultitenantTest {
t1 = new TenantIdentifier(null, "a1", null);
t2 = new TenantIdentifier(null, "a1", "t1");
t3 = new TenantIdentifier(null, "a1", "t2");
t4 = new TenantIdentifier(null, "a1", "t3");
TestCase[] testCases = new TestCase[]{
new TestCase(new TestCaseStep[]{
/* 0 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test@example.com"),
new CreatePlessUserWithEmail(t2, "test@example.com"),
new MakePrimaryUser(t1, 0),
new AssociateUserToTenant(t2, 0),
}),
new TestCase(new TestCaseStep[]{
/* 1 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test@example.com"),
new CreatePlessUserWithEmail(t2, "test@example.com"),
new AssociateUserToTenant(t2, 0),
new MakePrimaryUser(t1, 0),
}),
new TestCase(new TestCaseStep[]{
/* 2 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test@example.com"),
new CreatePlessUserWithEmail(t2, "test@example.com"),
new MakePrimaryUser(t1, 0),
@ -398,7 +407,7 @@ public class MultitenantTest {
new SignInEmailPasswordUser(t2, 0).expect(new WrongCredentialsException())
}),
new TestCase(new TestCaseStep[]{
/* 3 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -408,7 +417,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t2, 2), // Allowed
}),
new TestCase(new TestCaseStep[]{
/* 4 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -419,7 +428,7 @@ public class MultitenantTest {
new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
}),
new TestCase(new TestCaseStep[]{
/* 5 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -430,7 +439,7 @@ public class MultitenantTest {
new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException("", "")),
}),
new TestCase(new TestCaseStep[]{
/* 6 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -440,7 +449,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t2, 2), // Allowed
}),
new TestCase(new TestCaseStep[]{
/* 7 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -451,7 +460,7 @@ public class MultitenantTest {
new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
}),
new TestCase(new TestCaseStep[]{
/* 8 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -462,7 +471,7 @@ public class MultitenantTest {
new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException("", "")),
}),
new TestCase(new TestCaseStep[]{
/* 9 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -472,7 +481,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t2, 2), // Allowed
}),
new TestCase(new TestCaseStep[]{
/* 10 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -483,7 +492,7 @@ public class MultitenantTest {
new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
}),
new TestCase(new TestCaseStep[]{
/* 11 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -494,7 +503,7 @@ public class MultitenantTest {
new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException("", "")),
}),
new TestCase(new TestCaseStep[]{
/* 12 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t2, "test2@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -503,7 +512,7 @@ public class MultitenantTest {
new UpdatePlessUserEmail(t1, 0, "test2@example.com"),
}),
new TestCase(new TestCaseStep[]{
/* 13 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t1, "test3@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -511,10 +520,10 @@ public class MultitenantTest {
new LinkAccounts(t1, 0, 2),
new MakePrimaryUser(t2, 1),
new UpdatePlessUserEmail(t1, 0, "test3@example.com").expect(
new EmailChangeNotAllowedException()),
new DuplicateEmailException()),
}),
new TestCase(new TestCaseStep[]{
/* 14 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t1, "test3@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -522,10 +531,10 @@ public class MultitenantTest {
new LinkAccounts(t1, 0, 2),
new MakePrimaryUser(t2, 1),
new UpdatePlessUserEmail(t1, 1, "test1@example.com").expect(
new EmailChangeNotAllowedException()),
new DuplicateEmailException()),
}),
new TestCase(new TestCaseStep[]{
/* 15 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithPhone(t1, "+1000001"),
new CreatePlessUserWithPhone(t1, "+1000003"),
new CreatePlessUserWithPhone(t2, "+1000002"),
@ -535,7 +544,7 @@ public class MultitenantTest {
new UpdatePlessUserPhone(t1, 0, "+1000003").expect(new PhoneNumberChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 16 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithPhone(t1, "+1000001"),
new CreatePlessUserWithPhone(t1, "+1000003"),
new CreatePlessUserWithPhone(t2, "+1000002"),
@ -545,7 +554,7 @@ public class MultitenantTest {
new UpdatePlessUserPhone(t1, 1, "+1000001").expect(new PhoneNumberChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 17 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t1, "test3@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -553,10 +562,10 @@ public class MultitenantTest {
new LinkAccounts(t1, 0, 2),
new MakePrimaryUser(t2, 1),
new UpdateEmailPasswordUserEmail(t1, 0, "test3@example.com").expect(
new EmailChangeNotAllowedException()),
new DuplicateEmailException()),
}),
new TestCase(new TestCaseStep[]{
/* 18 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t1, "test3@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -564,10 +573,10 @@ public class MultitenantTest {
new LinkAccounts(t1, 0, 2),
new MakePrimaryUser(t2, 1),
new UpdateEmailPasswordUserEmail(t1, 1, "test1@example.com").expect(
new EmailChangeNotAllowedException()),
new DuplicateEmailException()),
}),
new TestCase(new TestCaseStep[]{
/* 19 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateThirdPartyUser(t2, "google", "googleid", "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -577,7 +586,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t2, 2), // Allowed
}),
new TestCase(new TestCaseStep[]{
/* 20 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateThirdPartyUser(t2, "google", "googleid", "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -588,7 +597,7 @@ public class MultitenantTest {
new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
}),
new TestCase(new TestCaseStep[]{
/* 21 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateThirdPartyUser(t2, "google", "googleid", "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -599,7 +608,7 @@ public class MultitenantTest {
new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException("", "")),
}),
new TestCase(new TestCaseStep[]{
/* 22 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid", "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -609,7 +618,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t2, 2), // Allowed
}),
new TestCase(new TestCaseStep[]{
/* 23 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid", "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -620,7 +629,7 @@ public class MultitenantTest {
new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
}),
new TestCase(new TestCaseStep[]{
/* 24 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid", "test1@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -631,7 +640,7 @@ public class MultitenantTest {
new AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException("", "")),
}),
new TestCase(new TestCaseStep[]{
/* 25 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid1", "test1@example.com"),
new CreateThirdPartyUser(t2, "google", "googleid2", "test2@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -640,7 +649,7 @@ public class MultitenantTest {
new CreateThirdPartyUser(t1, "google", "googleid1", "test2@example.com"),
}),
new TestCase(new TestCaseStep[]{
/* 26 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid1", "test1@example.com"),
new CreateThirdPartyUser(t1, "google", "googleid3", "test3@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -651,7 +660,7 @@ public class MultitenantTest {
new EmailChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 27 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid1", "test1@example.com"),
new CreateThirdPartyUser(t1, "google", "googleid3", "test3@example.com"),
new CreateEmailPasswordUser(t2, "test2@example.com"),
@ -662,30 +671,30 @@ public class MultitenantTest {
new EmailChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 28 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithPhone(t1, "+1000001"),
new CreatePlessUserWithPhone(t2, "+1000002"),
new CreatePlessUserWithPhone(t3, "+1000001"),
new MakePrimaryUser(t1, 0),
new LinkAccounts(t1, 0, 1),
new MakePrimaryUser(t3, 2),
new AssociateUserToTenant(t1, 2).expect(new DuplicatePhoneNumberException()),
new AssociateUserToTenant(t1, 2).expect(new AnotherPrimaryUserWithPhoneNumberAlreadyExistsException("")),
new AssociateUserToTenant(t2, 2).expect(
new AnotherPrimaryUserWithPhoneNumberAlreadyExistsException("")),
}),
new TestCase(new TestCaseStep[]{
/* 29 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid1", "test1@example.com"),
new CreateThirdPartyUser(t2, "google", "googleid2", "test2@example.com"),
new CreateThirdPartyUser(t3, "google", "googleid1", "test3@example.com"),
new MakePrimaryUser(t1, 0),
new LinkAccounts(t1, 0, 1),
new MakePrimaryUser(t3, 2),
new AssociateUserToTenant(t1, 2).expect(new DuplicateThirdPartyUserException()),
new AssociateUserToTenant(t1, 2).expect(new io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException("")),
new AssociateUserToTenant(t2, 2).expect(
new AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException("")),
new io.supertokens.pluginInterface.authRecipe.exceptions.AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException("")),
}),
new TestCase(new TestCaseStep[]{
/* 30 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid1", "test1@example.com"),
new CreateThirdPartyUser(t2, "google", "googleid2", "test2@example.com"),
new CreateThirdPartyUser(t1, "google", "googleid3", "test3@example.com"),
@ -697,7 +706,7 @@ public class MultitenantTest {
new CreateThirdPartyUser(t1, "google", "googleid3", "test1@example.com").expect(
new EmailChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 31 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -706,7 +715,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t2, 0).expect(new UnknownUserIdException()),
}),
new TestCase(new TestCaseStep[]{
/* 32 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test@example.com"),
new CreatePlessUserWithEmail(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -715,7 +724,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t2, 0).expect(new UnknownUserIdException()),
}),
new TestCase(new TestCaseStep[]{
/* 33 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "googleid1", "test@example.com"),
new CreateThirdPartyUser(t1, "google", "googleid2", "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -723,7 +732,7 @@ public class MultitenantTest {
new UnlinkAccount(t1, 0),
new AssociateUserToTenant(t2, 0).expect(new UnknownUserIdException()),
}),
new TestCase(new TestCaseStep[]{
/* 34 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -743,7 +752,7 @@ public class MultitenantTest {
}
}),
new TestCase(new TestCaseStep[]{
/* 35 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test@example.com"),
new DisassociateUserFromTenant(t1, 0),
new CreateEmailPasswordUser(t1, "test@example.com"),
@ -756,7 +765,7 @@ public class MultitenantTest {
new RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException(null, "")),
}),
new TestCase(new TestCaseStep[]{
/* 36 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test@example.com"),
new DisassociateUserFromTenant(t1, 0),
new CreateEmailPasswordUser(t1, "test@example.com"),
@ -767,7 +776,7 @@ public class MultitenantTest {
new AssociateUserToTenant(t1, 1).expect(new DuplicateEmailException()),
new AssociateUserToTenant(t2, 1),
}),
new TestCase(new TestCaseStep[]{
/* 37 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -792,33 +801,33 @@ public class MultitenantTest {
}
}),
new TestCase(new TestCaseStep[]{
/* 38 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
new UpdatePlessUserEmail(t1, 1, "test1@example.com"),
}),
new TestCase(new TestCaseStep[]{
/* 39 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
new UpdateEmailPasswordUserEmail(t1, 1, "test1@example.com"),
}),
new TestCase(new TestCaseStep[]{
/* 40 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t1, "test2@example.com"),
new MakePrimaryUser(t1, 1),
new UpdatePlessUserEmail(t1, 1, "test1@example.com"),
}),
new TestCase(new TestCaseStep[]{
/* 41 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 1),
new UpdateEmailPasswordUserEmail(t1, 1, "test1@example.com"),
}),
new TestCase(new TestCaseStep[]{
/* 42 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -826,7 +835,7 @@ public class MultitenantTest {
new UpdatePlessUserEmail(t1, 1, "test1@example.com").expect(
new EmailChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 43 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -835,19 +844,19 @@ public class MultitenantTest {
new EmailChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 44 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateThirdPartyUser(t1, "google", "googleid", "test2@example.com"),
new MakePrimaryUser(t1, 0),
new CreateThirdPartyUser(t1, "google", "googleid", "test2@example.com"), // allowed
}),
new TestCase(new TestCaseStep[]{
/* 45 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateThirdPartyUser(t1, "google", "googleid", "test2@example.com"),
new MakePrimaryUser(t1, 1),
new CreateThirdPartyUser(t1, "google", "googleid", "test2@example.com"), // allowed
}),
new TestCase(new TestCaseStep[]{
/* 46 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateThirdPartyUser(t1, "google", "googleid", "test2@example.com"),
new MakePrimaryUser(t1, 0),
@ -855,7 +864,7 @@ public class MultitenantTest {
new CreateThirdPartyUser(t1, "google", "googleid", "test1@example.com").expect(
new EmailChangeNotAllowedException()),
}),
new TestCase(new TestCaseStep[]{
/* 47 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test3@example.com"),
new MakePrimaryUser(t1, 0),
@ -865,6 +874,38 @@ public class MultitenantTest {
new CreateThirdPartyUser(t2, "google", "googleid", "test1@example.com").expect(
new EmailChangeNotAllowedException()),
}),
/* 48 */ new TestCase(new TestCaseStep[]{
new CreateEmailPasswordUser(t1, "test1@example.com"),
new CreateEmailPasswordUser(t2, "test3@example.com"),
new CreatePlessUserWithEmail(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
new LinkAccounts(t1, 0, 2),
new MakePrimaryUser(t2, 1),
new UpdateEmailPasswordUserEmail(t2, 1, "test2@example.com"),
new AssociateUserToTenant(t1, 1).expect(new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
}),
/* 49 */ new TestCase(new TestCaseStep[]{
new CreatePlessUserWithEmail(t1, "test1@example.com"),
new CreatePlessUserWithEmail(t2, "test3@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
new LinkAccounts(t1, 0, 2),
new MakePrimaryUser(t2, 1),
new UpdatePlessUserEmail(t2, 1, "test2@example.com"),
new AssociateUserToTenant(t1, 1).expect(new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
}),
/* 50 */ new TestCase(new TestCaseStep[]{
new CreateThirdPartyUser(t1, "google", "gid1", "test1@example.com"),
new CreateThirdPartyUser(t2, "google", "gid3", "test3@example.com"),
new CreateEmailPasswordUser(t1, "test2@example.com"),
new MakePrimaryUser(t1, 0),
new LinkAccounts(t1, 0, 2),
new MakePrimaryUser(t2, 1),
new UpdateThirdPartyUserEmail(t2, "google", "gid3", "test2@example.com"),
new AssociateUserToTenant(t1, 1).expect(new AnotherPrimaryUserWithEmailAlreadyExistsException("")),
})
};
int i = 0;
@ -933,7 +974,10 @@ public class MultitenantTest {
this.execute(main);
fail();
} catch (Exception e) {
assertEquals(this.e.getClass(), e.getClass());
if (!this.e.getClass().equals(e.getClass())) {
throw new RuntimeException(e);
}
// assertEquals(this.e.getClass(), e.getClass());
}
}
}
@ -1139,6 +1183,27 @@ public class MultitenantTest {
}
}
private static class UpdateThirdPartyUserEmail extends TestCaseStep {
private final TenantIdentifier tenantIdentifier;
private final String thirdPartyId;
private final String thirdPartyUserId;
private final String email;
public UpdateThirdPartyUserEmail(TenantIdentifier tenantIdentifier, String thirdPartyId, String thirdPartyUserId,
String email) {
this.tenantIdentifier = tenantIdentifier;
this.thirdPartyId = thirdPartyId;
this.thirdPartyUserId = thirdPartyUserId;
this.email = email;
}
@Override
public void execute(Main main) throws Exception {
Storage storage = (StorageLayer.getStorage(tenantIdentifier, main));
ThirdParty.signInUp(tenantIdentifier, storage, main, thirdPartyId, thirdPartyUserId, email);
}
}
private static class UnlinkAccount extends TestCaseStep {
TenantIdentifier tenantIdentifier;
int userIndex;

View File

@ -22,7 +22,7 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;

View File

@ -22,7 +22,7 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;

View File

@ -22,7 +22,7 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;

View File

@ -21,7 +21,7 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;

View File

@ -17,13 +17,12 @@
package io.supertokens.test.accountlinking.api;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;

View File

@ -21,7 +21,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.supertokens.ProcessState;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;

View File

@ -23,7 +23,7 @@ import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.UserPaginationContainer;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;

View File

@ -22,7 +22,7 @@ import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.config.Config;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.PasswordHashing;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.emailpassword.exceptions.ResetPasswordInvalidTokenException;
import io.supertokens.emailpassword.exceptions.WrongCredentialsException;
import io.supertokens.featureflag.EE_FEATURES;
@ -925,7 +925,7 @@ public class EmailPasswordTest {
EmailPassword.updateUsersEmailOrPassword(process.getProcess(), user.getSupertokensUserId(), "someemail1@gmail.com",
null);
assert (false);
} catch (EmailChangeNotAllowedException ignored) {
} catch (DuplicateEmailException ignored) {
}
@ -1011,7 +1011,7 @@ public class EmailPasswordTest {
EmailPassword.updateUsersEmailOrPassword(process.getProcess(), user.getSupertokensUserId(), "someemail1@gmail.com",
null);
assert (false);
} catch (EmailChangeNotAllowedException ignored) {
} catch (DuplicateEmailException ignored) {
}

View File

@ -19,7 +19,7 @@ package io.supertokens.test.emailpassword;
import com.google.gson.JsonObject;
import io.supertokens.ProcessState;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.emailpassword.exceptions.WrongCredentialsException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
@ -369,15 +369,17 @@ public class MultitenantEmailPasswordTest {
EmailChangeNotAllowedException {
String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.startIsolatedProcess(args);
TestingProcessManager.TestingProcess process = TestingProcessManager.startIsolatedProcess(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY});
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
return;
}
createTenants(process);
TenantIdentifier t1 = new TenantIdentifier(null, process.getAppForTesting().getAppId() + "a1", null);

View File

@ -83,9 +83,8 @@ public class UserPutAPITest4_0 {
"http://localhost:3567/recipe/user", body, 1000, 1000, null, SemVer.v4_0.get(),
RECIPE_ID.EMAIL_PASSWORD.toString());
assertEquals("EMAIL_CHANGE_NOT_ALLOWED_ERROR", response.get("status").getAsString());
assertEquals("New email is associated with another primary user ID", response.get("reason").getAsString());
assertEquals(2, response.entrySet().size());
assertEquals("EMAIL_ALREADY_EXISTS_ERROR", response.get("status").getAsString());
assertEquals(1, response.entrySet().size());
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));

View File

@ -296,7 +296,7 @@ public class HttpRequestForTesting {
con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod(method);
con.setConnectTimeout(connectionTimeoutMS);
con.setReadTimeout(readTimeoutMS + 1000);
con.setReadTimeout(readTimeoutMS);
con.setInstanceFollowRedirects(followRedirects);
con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
if (version != null) {

View File

@ -24,6 +24,8 @@ import java.util.Arrays;
import javax.crypto.spec.SecretKeySpec;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import org.apache.commons.codec.binary.Base32;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
@ -67,7 +69,6 @@ import io.supertokens.pluginInterface.webauthn.AccountRecoveryTokenInfo;
import io.supertokens.pluginInterface.webauthn.WebAuthNOptions;
import io.supertokens.pluginInterface.webauthn.WebAuthNStorage;
import io.supertokens.pluginInterface.webauthn.WebAuthNStoredCredential;
import io.supertokens.pluginInterface.webauthn.exceptions.DuplicateUserEmailException;
import io.supertokens.pluginInterface.webauthn.exceptions.DuplicateUserIdException;
import io.supertokens.pluginInterface.webauthn.slqStorage.WebAuthNSQLStorage;
import io.supertokens.session.Session;
@ -154,6 +155,7 @@ public class TestAppData {
"password");
EmailPassword.generatePasswordResetTokenBeforeCdi4_0(app, appStorage, process.getProcess(),
epUser.getSupertokensUserId());
AuthRecipe.createPrimaryUser(process.getProcess(), app.toAppIdentifier(), appStorage, epUser.getSupertokensUserId());
ThirdParty.SignInUpResponse tpUser = ThirdParty.signInUp(app, appStorage, process.getProcess(), "google",
"googleid", "test@example.com");
@ -225,7 +227,7 @@ public class TestAppData {
((WebAuthNSQLStorage) appStorage).signUpWithCredentialsRegister_Transaction(app, con, "userId", "test2@example.com", "example.com", credential);
} catch (DuplicateUserIdException e) {
throw new RuntimeException(e);
} catch (DuplicateUserEmailException e) {
} catch (DuplicateEmailException e) {
throw new RuntimeException(e);
}
return null;

View File

@ -90,7 +90,7 @@ public class TestMultitenancyAPIHelper {
throws HttpResponseException, IOException {
JsonObject response = HttpRequestForTesting.sendGETRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(sourceTenant, "/recipe/multitenancy/connectionuridomain/list"),
null, 1000, 1000, null,
null, 1000, 2500, null,
SemVer.v3_0.get(), "multitenancy");
assertEquals("OK", response.getAsJsonPrimitive("status").getAsString());
@ -160,7 +160,7 @@ public class TestMultitenancyAPIHelper {
throws HttpResponseException, IOException {
JsonObject response = HttpRequestForTesting.sendGETRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(sourceTenant, "/recipe/multitenancy/app/list"),
null, 1000, 1000, null,
null, 1000, 2500, null,
SemVer.v3_0.get(), "multitenancy");
assertEquals("OK", response.getAsJsonPrimitive("status").getAsString());
@ -229,7 +229,7 @@ public class TestMultitenancyAPIHelper {
throws HttpResponseException, IOException {
JsonObject response = HttpRequestForTesting.sendGETRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(sourceTenant, "/recipe/multitenancy/tenant/list"),
null, 1000, 1000, null,
null, 1000, 2500, null,
SemVer.v3_0.get(), "multitenancy");
assertEquals("OK", response.getAsJsonPrimitive("status").getAsString());
@ -260,7 +260,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendGETRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/multitenancy/tenant"),
null, 1000, 1000, null,
null, 1000, 2500, null,
version.get(), "multitenancy");
assertEquals("OK", response.getAsJsonPrimitive("status").getAsString());
@ -274,7 +274,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/multitenancy/tenant/user"),
requestBody, 1000, 1000, null,
requestBody, 1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), "multitenancy");
return response;
@ -287,7 +287,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/multitenancy/tenant/user/remove"),
requestBody, 1000, 1000, null,
requestBody, 1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), "multitenancy");
assertEquals("OK", response.getAsJsonPrimitive("status").getAsString());
@ -348,7 +348,7 @@ public class TestMultitenancyAPIHelper {
}
JsonObject response = HttpRequestForTesting.sendGETRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(sourceTenant, "/users"),
params, 1000, 1000, null,
params, 1000, 2500, null,
SemVer.v3_0.get(), null);
assertEquals("OK", response.getAsJsonPrimitive("status").getAsString());
@ -373,7 +373,7 @@ public class TestMultitenancyAPIHelper {
requestBody.addProperty("password", password);
JsonObject signUpResponse = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/signup"),
requestBody, 1000, 1000, null,
requestBody, 1000, 2500, null,
version.get(), "emailpassword");
return signUpResponse;
}
@ -386,7 +386,7 @@ public class TestMultitenancyAPIHelper {
requestBody.addProperty("password", password);
JsonObject signUpResponse = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/signin"),
requestBody, 1000, 1000, null,
requestBody, 1000, 2500, null,
version.get(), "emailpassword");
return signUpResponse;
}
@ -424,7 +424,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/signinup"), signUpRequestBody,
1000, 1000, null,
1000, 2500, null,
version.get(), "thirdparty");
return response;
}
@ -456,7 +456,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/signinup/code"),
createCodeRequestBody, 1000, 1000, null,
createCodeRequestBody, 1000, 2500, null,
version.get(), "passwordless");
assertEquals("OK", response.get("status").getAsString());
@ -495,7 +495,7 @@ public class TestMultitenancyAPIHelper {
return HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/signinup/code/consume"),
consumeCodeRequestBody, 1000, 1000, null,
consumeCodeRequestBody, 1000, 2500, null,
version.get(), "passwordless");
}
@ -508,7 +508,7 @@ public class TestMultitenancyAPIHelper {
return HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/signinup/code/consume"),
consumeCodeRequestBody, 1000, 1000, null,
consumeCodeRequestBody, 1000, 2500, null,
version.get(), "passwordless");
}
@ -548,7 +548,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/signinup/code"),
createCodeRequestBody, 1000, 1000, null,
createCodeRequestBody, 1000, 2500, null,
version.get(), "passwordless");
assertEquals("OK", response.get("status").getAsString());
@ -594,7 +594,7 @@ public class TestMultitenancyAPIHelper {
public static void removeLicense(Main main) throws HttpResponseException, IOException {
JsonObject response = HttpRequestForTesting.sendJsonDELETERequest(main, "",
"http://localhost:3567/ee/license", null,
1000, 1000, null,
1000, 2500, null,
SemVer.v3_0.get(), null);
assertEquals("OK", response.get("status").getAsString());
}
@ -605,7 +605,7 @@ public class TestMultitenancyAPIHelper {
map.put("userId", userId);
JsonObject userResponse = HttpRequestForTesting.sendGETRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user"),
map, 1000, 1000, null, SemVer.v3_0.get(),
map, 1000, 2500, null, SemVer.v3_0.get(),
"emailpassword");
assertEquals("OK", userResponse.getAsJsonPrimitive("status").getAsString());
return userResponse.getAsJsonObject("user");
@ -620,7 +620,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/userid/map"), requestBody,
1000, 1000, null,
1000, 2500, null,
SemVer.v3_0.get(), "useridmapping");
assertEquals("OK", response.get("status").getAsString());
}
@ -631,7 +631,7 @@ public class TestMultitenancyAPIHelper {
params.put("userId", userId);
JsonObject response = HttpRequestForTesting.sendGETRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/user/id"),
params, 1000, 1000, null,
params, 1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), "");
return response;
}
@ -644,7 +644,7 @@ public class TestMultitenancyAPIHelper {
requestBody.add("metadataUpdate", metadata);
JsonObject resp = HttpRequestForTesting.sendJsonPUTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user/metadata"),
requestBody, 1000, 1000, null,
requestBody, 1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), "usermetadata");
return resp;
}
@ -655,7 +655,7 @@ public class TestMultitenancyAPIHelper {
requestBody.addProperty("userId", userId);
JsonObject resp = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user/metadata/remove"),
requestBody, 1000, 1000, null,
requestBody, 1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), "usermetadata");
return resp;
@ -667,7 +667,7 @@ public class TestMultitenancyAPIHelper {
requestBody.addProperty("role", role);
JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/role"),
requestBody, 1000, 1000, null, WebserverAPI.getLatestCDIVersion().get(),
requestBody, 1000, 2500, null, WebserverAPI.getLatestCDIVersion().get(),
"userroles");
assertEquals("OK", response.get("status").getAsString());
}
@ -679,7 +679,7 @@ public class TestMultitenancyAPIHelper {
requestBody.addProperty("userId", userId);
JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user/role"), requestBody, 1000, 1000,
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user/role"), requestBody, 1000, 2500,
null,
WebserverAPI.getLatestCDIVersion().get(), "userroles");
@ -704,7 +704,7 @@ public class TestMultitenancyAPIHelper {
request.addProperty("role", role);
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/role/remove"), request, 1000, 1000,
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/role/remove"), request, 1000, 2500,
null,
WebserverAPI.getLatestCDIVersion().get(), "userroles");
assertEquals(2, response.entrySet().size());
@ -719,7 +719,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user/email/verify/token"),
requestBody, 1000, 1000, null,
requestBody, 1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), "emailverification");
assertEquals(response.entrySet().size(), 2);
@ -731,7 +731,7 @@ public class TestMultitenancyAPIHelper {
JsonObject response2 = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user/email/verify"),
verifyResponseBody, 1000, 1000, null,
verifyResponseBody, 1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), "emailverification");
assertEquals(response2.entrySet().size(), 3);
@ -746,7 +746,7 @@ public class TestMultitenancyAPIHelper {
HttpRequestForTesting.sendJsonPOSTRequest(main, "",
HttpRequestForTesting.getMultitenantUrl(tenantIdentifier, "/recipe/user/email/verify/remove"), body,
1000, 1000, null,
1000, 2500, null,
WebserverAPI.getLatestCDIVersion().get(), RECIPE_ID.EMAIL_VERIFICATION.toString());
}
}

View File

@ -22,7 +22,7 @@ import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;

View File

@ -32,28 +32,28 @@ public class OAuthAPIHelper {
public static JsonObject createClient(Main main, JsonObject createClientBody) throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
"http://localhost:3567/recipe/oauth/clients", createClientBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/clients", createClientBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
public static JsonObject updateClient(Main main, JsonObject updateClientBody) throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(main, "",
"http://localhost:3567/recipe/oauth/clients", updateClientBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/clients", updateClientBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
public static JsonObject auth(Main main, JsonObject authBody) throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
"http://localhost:3567/recipe/oauth/auth", authBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/auth", authBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
public static JsonObject token(Main main, JsonObject tokenBody) throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
"http://localhost:3567/recipe/oauth/token", tokenBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/token", tokenBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
@ -74,7 +74,7 @@ public class OAuthAPIHelper {
}
JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(main, "",
url, acceptLoginChallengeBody, 5000, 5000, null,
url, acceptLoginChallengeBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
@ -95,28 +95,28 @@ public class OAuthAPIHelper {
}
JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(main, "",
url, acceptConsentChallengeBody, 5000, 5000, null,
url, acceptConsentChallengeBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
public static JsonObject revoke(Main main, JsonObject revokeRequestBody) throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
"http://localhost:3567/recipe/oauth/token/revoke", revokeRequestBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/token/revoke", revokeRequestBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
public static JsonObject introspect(Main main, JsonObject introspectRequestBody) throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
"http://localhost:3567/recipe/oauth/introspect", introspectRequestBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/introspect", introspectRequestBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
public static JsonObject revokeClientId(Main main, JsonObject revokeClientIdRequestBody) throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
"http://localhost:3567/recipe/oauth/tokens/revoke", revokeClientIdRequestBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/tokens/revoke", revokeClientIdRequestBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}
@ -124,7 +124,7 @@ public class OAuthAPIHelper {
public static JsonObject revokeSessionHandle(Main main, JsonObject revokeSessionHandleRequestBody)
throws Exception {
JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(main, "",
"http://localhost:3567/recipe/oauth/session/revoke", revokeSessionHandleRequestBody, 5000, 5000, null,
"http://localhost:3567/recipe/oauth/session/revoke", revokeSessionHandleRequestBody, 1000, 15000, null,
SemVer.v5_2.get(), "");
return response;
}

View File

@ -19,6 +19,7 @@ package io.supertokens.test.passwordless;
import io.supertokens.ProcessState;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException;
import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException;
@ -381,7 +382,7 @@ public class PasswordlessStorageTest {
try {
storage.updateUserEmail_Transaction(process.getAppForTesting().toAppIdentifier(), con, userIdNotExists,
email3);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
storage.commitTransaction(con);
@ -425,7 +426,7 @@ public class PasswordlessStorageTest {
try {
storage.updateUserEmail_Transaction(
process.getAppForTesting().toAppIdentifier(), con, userIdEmail1, email2);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
storage.commitTransaction(con);
@ -447,7 +448,7 @@ public class PasswordlessStorageTest {
storage.startTransaction(con -> {
try {
storage.updateUserEmail_Transaction(process.getAppForTesting().toAppIdentifier(), con, userIdEmail1, email2);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
storage.commitTransaction(con);
@ -517,7 +518,7 @@ public class PasswordlessStorageTest {
storage.startTransaction(con -> {
try {
storage.updateUserEmail_Transaction(process.getAppForTesting().toAppIdentifier(), con, userIdPhone1, email);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
storage.commitTransaction(con);
@ -568,7 +569,7 @@ public class PasswordlessStorageTest {
storage.startTransaction(con -> {
try {
storage.updateUserEmail_Transaction(process.getAppForTesting().toAppIdentifier(), con, userId, email2);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
storage.commitTransaction(con);
@ -579,7 +580,7 @@ public class PasswordlessStorageTest {
storage.startTransaction(con -> {
try {
storage.updateUserEmail_Transaction(process.getAppForTesting().toAppIdentifier(), con, userId, null);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
try {
@ -606,7 +607,7 @@ public class PasswordlessStorageTest {
storage.startTransaction(con -> {
try {
storage.updateUserEmail_Transaction(process.getAppForTesting().toAppIdentifier(), con, userId, email);
} catch (UnknownUserIdException | DuplicateEmailException e) {
} catch (UnknownUserIdException | DuplicateEmailException | EmailChangeNotAllowedException e) {
throw new StorageTransactionLogicException(e);
}
try {

View File

@ -21,6 +21,7 @@ import io.supertokens.passwordless.Passwordless;
import io.supertokens.passwordless.exceptions.UserWithoutContactInfoException;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;

View File

@ -21,7 +21,6 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.emailverification.EmailVerification;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
@ -37,7 +36,6 @@ import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.test.TestingProcessManager;
import io.supertokens.test.Utils;
import io.supertokens.test.httpRequest.HttpRequestForTesting;
import io.supertokens.thirdparty.ThirdParty;
import io.supertokens.utils.SemVer;
import org.junit.AfterClass;
import org.junit.Before;

View File

@ -21,7 +21,7 @@ import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.exceptions.EmailChangeNotAllowedException;
import io.supertokens.pluginInterface.authRecipe.exceptions.EmailChangeNotAllowedException;
import io.supertokens.emailverification.EmailVerification;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;

View File

@ -193,134 +193,110 @@ public class UserMetadataTest {
return;
}
String userId = "userId";
// Repeat concurrent test 100 times
for (int idx = 0; idx < 100; idx++) {
String userId = "userId" + idx;
JsonObject expected = new JsonObject();
JsonObject update1 = new JsonObject();
update1.addProperty("a", 1);
expected.addProperty("a", 1);
JsonObject expected = new JsonObject();
JsonObject update1 = new JsonObject();
update1.addProperty("a", 1);
expected.addProperty("a", 1);
JsonObject update2 = new JsonObject();
update2.addProperty("b", 2);
expected.addProperty("b", 2);
JsonObject update2 = new JsonObject();
update2.addProperty("b", 2);
expected.addProperty("b", 2);
UserMetadataSQLStorage sqlStorage = (UserMetadataSQLStorage) StorageLayer.getStorage(process.getProcess());
UserMetadataSQLStorage sqlStorage = (UserMetadataSQLStorage) StorageLayer.getStorage(process.getProcess());
AtomicReference<String> t1State = new AtomicReference<>("init");
AtomicReference<String> t2State = new AtomicReference<>("init");
final Object syncObject = new Object();
AtomicReference<String> t1State = new AtomicReference<>("init");
AtomicReference<String> t2State = new AtomicReference<>("init");
final Object syncObject = new Object();
AtomicInteger tryCount1 = new AtomicInteger(0);
AtomicInteger tryCount2 = new AtomicInteger(0);
AtomicBoolean success1 = new AtomicBoolean(false);
AtomicBoolean success2 = new AtomicBoolean(false);
AtomicInteger tryCount1 = new AtomicInteger(0);
AtomicInteger tryCount2 = new AtomicInteger(0);
AtomicBoolean success1 = new AtomicBoolean(false);
AtomicBoolean success2 = new AtomicBoolean(false);
AppIdentifier appIdentifier = process.getAppForTesting().toAppIdentifier();
AppIdentifier appIdentifier = process.getAppForTesting().toAppIdentifier();
Runnable r1 = () -> {
try {
sqlStorage.startTransaction(con -> {
tryCount1.incrementAndGet();
JsonObject originalMetadata = sqlStorage.getUserMetadata_Transaction(appIdentifier, con, userId);
Runnable r1 = () -> {
try {
sqlStorage.startTransaction(con -> {
tryCount1.incrementAndGet();
synchronized (syncObject) {
t1State.set("read");
syncObject.notifyAll();
}
JsonObject originalMetadata = sqlStorage.getUserMetadata_Transaction(appIdentifier, con, userId);
synchronized (syncObject) {
while (!t2State.get().equals("read")) {
try {
syncObject.wait();
} catch (InterruptedException e) {
}
JsonObject updatedMetadata = originalMetadata == null ? new JsonObject() : originalMetadata;
MetadataUtils.shallowMergeMetadataUpdate(updatedMetadata, update1);
try {
sqlStorage.setUserMetadata_Transaction(appIdentifier, con, userId,
updatedMetadata);
} catch (TenantOrAppNotFoundException e) {
throw new StorageTransactionLogicException(e);
}
sqlStorage.commitTransaction(con);
success1.set(true); // it should come here because we will try three times.
return null;
});
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw new IllegalStateException(e.actualException);
}
JsonObject updatedMetadata = originalMetadata == null ? new JsonObject() : originalMetadata;
MetadataUtils.shallowMergeMetadataUpdate(updatedMetadata, update1);
try {
sqlStorage.setUserMetadata_Transaction(appIdentifier, con, userId,
updatedMetadata);
} catch (TenantOrAppNotFoundException e) {
throw new StorageTransactionLogicException(e);
}
sqlStorage.commitTransaction(con);
success1.set(true); // it should come here because we will try three times.
return null;
});
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw new IllegalStateException(e.actualException);
} catch (Exception ignored) {
}
} catch (Exception ignored) {
}
};
};
Runnable r2 = () -> {
try {
sqlStorage.startTransaction(con -> {
tryCount2.incrementAndGet();
Runnable r2 = () -> {
try {
sqlStorage.startTransaction(con -> {
tryCount2.incrementAndGet();
JsonObject originalMetadata = sqlStorage.getUserMetadata_Transaction(appIdentifier, con, userId);
JsonObject originalMetadata = sqlStorage.getUserMetadata_Transaction(appIdentifier, con, userId);
synchronized (syncObject) {
t2State.set("read");
syncObject.notifyAll();
}
JsonObject updatedMetadata = originalMetadata == null ? new JsonObject() : originalMetadata;
MetadataUtils.shallowMergeMetadataUpdate(updatedMetadata, update2);
synchronized (syncObject) {
while (!t1State.get().equals("read")) {
try {
syncObject.wait();
} catch (InterruptedException e) {
}
try {
sqlStorage.setUserMetadata_Transaction(appIdentifier, con, userId,
updatedMetadata);
} catch (TenantOrAppNotFoundException e) {
throw new StorageTransactionLogicException(e);
}
sqlStorage.commitTransaction(con);
success2.set(true); // it should come here because we will try three times.
return null;
});
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw new IllegalStateException(e.actualException);
}
JsonObject updatedMetadata = originalMetadata == null ? new JsonObject() : originalMetadata;
MetadataUtils.shallowMergeMetadataUpdate(updatedMetadata, update2);
try {
sqlStorage.setUserMetadata_Transaction(appIdentifier, con, userId,
updatedMetadata);
} catch (TenantOrAppNotFoundException e) {
throw new StorageTransactionLogicException(e);
}
sqlStorage.commitTransaction(con);
success2.set(true); // it should come here because we will try three times.
return null;
});
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw new IllegalStateException(e.actualException);
} catch (Exception ignored) {
}
} catch (Exception ignored) {
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
t1.start();
t2.start();
t1.join(5000);
t2.join(5000);
t1.join(5000);
t2.join(5000);
// The empty row did not lock, so we check if the system found a deadlock and that we could resolve it.
// The empty row did not lock, so we check if the system found a deadlock and that we could resolve it.
// Both succeeds in the end
assertTrue(success1.get());
assertTrue(success2.get());
// Both succeeds in the end
assertTrue(success1.get());
assertTrue(success2.get());
// One of them had to be retried (not deterministic which)
assertEquals(3, tryCount1.get() + tryCount2.get());
// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
// One of them had to be retried (not deterministic which)
assertTrue(3 >= tryCount1.get() + tryCount2.get());
// assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
// The end result is as expected
assertEquals(expected, sqlStorage.getUserMetadata(appIdentifier, userId));
// The end result is as expected
assertEquals(expected, sqlStorage.getUserMetadata(appIdentifier, userId));
}
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));

View File

@ -29,7 +29,7 @@ import com.webauthn4j.test.client.ClientPlatform;
import com.webauthn4j.util.Base64UrlUtil;
import io.supertokens.Main;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;

View File

@ -18,7 +18,7 @@ package io.supertokens.test.webauthn;
import com.google.gson.JsonObject;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.pluginInterface.authRecipe.exceptions.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;