Skip to content

Commit

Permalink
fix: tests (#776)
Browse files Browse the repository at this point in the history
* fix: tests

* fix: removed removal of active user

* fix: user id mapping deletion

* fix: more fixes

* fix: user delete

* fix: test

* fix: test

* fix: session fix and thirdparty ev test

* fix: pr comments

* fix: pr comments

* fix: tests

* fix: phone number change related
  • Loading branch information
sattvikc authored Sep 6, 2023
1 parent de76ce8 commit 3e39d0b
Show file tree
Hide file tree
Showing 22 changed files with 1,131 additions and 368 deletions.
15 changes: 11 additions & 4 deletions src/main/java/io/supertokens/authRecipe/AuthRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ private static CreatePrimaryUserResult canCreatePrimaryUserHelper(TransactionCon
.listPrimaryUsersByPhoneNumber_Transaction(appIdentifierWithStorage, 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" +
Expand Down Expand Up @@ -817,6 +820,10 @@ private static void deleteUserHelper(TransactionConnection con, AppIdentifierWit
if (removeAllLinkedAccounts || userToDelete.loginMethods.length == 1) {
if (userToDelete.getSupertokensUserId().equals(userIdToDeleteForAuthRecipe)) {
primaryUserIdToDeleteNonAuthRecipe = userIdToDeleteForNonAuthRecipeForRecipeUserId;
if (primaryUserIdToDeleteNonAuthRecipe == null) {
deleteAuthRecipeUser(con, appIdentifierWithStorage, userToDelete.getSupertokensUserId(),
true);
}
} else {
// this is always type supertokens user ID cause it's from a user from the database.
io.supertokens.pluginInterface.useridmapping.UserIdMapping mappingResult =
Expand Down Expand Up @@ -918,15 +925,15 @@ private static void deleteNonAuthRecipeUser(TransactionConnection con, AppIdenti

private static void deleteAuthRecipeUser(TransactionConnection con,
AppIdentifierWithStorage appIdentifierWithStorage, String
userId, boolean deleteUserIdMappingToo)
userId, boolean deleteFromUserIdToAppIdTableToo)
throws StorageQueryException {
// auth recipe deletions here only
appIdentifierWithStorage.getEmailPasswordStorage()
.deleteEmailPasswordUser_Transaction(con, appIdentifierWithStorage, userId, deleteUserIdMappingToo);
.deleteEmailPasswordUser_Transaction(con, appIdentifierWithStorage, userId, deleteFromUserIdToAppIdTableToo);
appIdentifierWithStorage.getThirdPartyStorage()
.deleteThirdPartyUser_Transaction(con, appIdentifierWithStorage, userId, deleteUserIdMappingToo);
.deleteThirdPartyUser_Transaction(con, appIdentifierWithStorage, userId, deleteFromUserIdToAppIdTableToo);
appIdentifierWithStorage.getPasswordlessStorage()
.deletePasswordlessUser_Transaction(con, appIdentifierWithStorage, userId, deleteUserIdMappingToo);
.deletePasswordlessUser_Transaction(con, appIdentifierWithStorage, userId, deleteFromUserIdToAppIdTableToo);
}

public static boolean deleteNonAuthRecipeUser(TenantIdentifierWithStorage
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/supertokens/inmemorydb/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -2786,6 +2786,14 @@ public AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(AppIdentif
// }
}

@Override
public AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo(AppIdentifier appIdentifier,
String thirdPartyId,
String thirdPartyUserId)
throws StorageQueryException {
return null; // TODO
}

@Override
public AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(AppIdentifier appIdentifier,
TransactionConnection con,
Expand Down
74 changes: 69 additions & 5 deletions src/main/java/io/supertokens/multitenancy/Multitenancy.java
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,9 @@ public static boolean addUserIdToTenant(Main main, TenantIdentifierWithStorage t
String userId)
throws TenantOrAppNotFoundException, UnknownUserIdException, StorageQueryException,
FeatureNotEnabledException, DuplicateEmailException, DuplicatePhoneNumberException,
DuplicateThirdPartyUserException {
DuplicateThirdPartyUserException, AnotherPrimaryUserWithPhoneNumberAlreadyExistsException,
AnotherPrimaryUserWithEmailAlreadyExistsException,
AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException {
if (Arrays.stream(FeatureFlag.getInstance(main, new AppIdentifier(null, null)).getEnabledFeatures())
.noneMatch(ee_features -> ee_features == EE_FEATURES.MULTI_TENANCY)) {
throw new FeatureNotEnabledException(EE_FEATURES.MULTI_TENANCY);
Expand All @@ -396,7 +398,7 @@ public static boolean addUserIdToTenant(Main main, TenantIdentifierWithStorage t
String tenantId = tenantIdentifierWithStorage.getTenantId();
AuthRecipeUserInfo userToAssociate = storage.getPrimaryUserById_Transaction(tenantIdentifierWithStorage.toAppIdentifier(), con, userId);

if (userToAssociate.isPrimaryUser) {
if (userToAssociate != null && userToAssociate.isPrimaryUser) {
Set<String> emails = new HashSet<>();
Set<String> phoneNumbers = new HashSet<>();
Set<LoginMethod.ThirdParty> thirdParties = new HashSet<>();
Expand All @@ -418,7 +420,16 @@ public static boolean addUserIdToTenant(Main main, TenantIdentifierWithStorage t
AuthRecipeUserInfo[] users = storage.listPrimaryUsersByEmail_Transaction(tenantIdentifierWithStorage.toAppIdentifier(), con, email);
for (AuthRecipeUserInfo user : users) {
if (user.tenantIds.contains(tenantId) && !user.getSupertokensUserId().equals(userId)) {
throw new StorageTransactionLogicException(new DuplicateEmailException());
for (LoginMethod lm1 : user.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(user.getSupertokensUserId()));
}
}
}
Expand All @@ -427,7 +438,16 @@ public static boolean addUserIdToTenant(Main main, TenantIdentifierWithStorage t
AuthRecipeUserInfo[] users = storage.listPrimaryUsersByPhoneNumber_Transaction(tenantIdentifierWithStorage.toAppIdentifier(), con, phoneNumber);
for (AuthRecipeUserInfo user : users) {
if (user.tenantIds.contains(tenantId) && !user.getSupertokensUserId().equals(userId)) {
throw new StorageTransactionLogicException(new DuplicatePhoneNumberException());
for (LoginMethod lm1 : user.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(user.getSupertokensUserId()));
}
}
}
Expand All @@ -436,12 +456,25 @@ public static boolean addUserIdToTenant(Main main, TenantIdentifierWithStorage t
AuthRecipeUserInfo[] users = storage.listPrimaryUsersByThirdPartyInfo_Transaction(tenantIdentifierWithStorage.toAppIdentifier(), con, tp.id, tp.userId);
for (AuthRecipeUserInfo user : users) {
if (user.tenantIds.contains(tenantId) && !user.getSupertokensUserId().equals(userId)) {
throw new StorageTransactionLogicException(new DuplicateThirdPartyUserException());
for (LoginMethod lm1 : user.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(user.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 {
boolean result = ((MultitenancySQLStorage) storage).addUserIdToTenant_Transaction(tenantIdentifierWithStorage, con, userId);
storage.commitTransaction(con);
Expand All @@ -462,20 +495,51 @@ public static boolean addUserIdToTenant(Main main, TenantIdentifierWithStorage t
throw (TenantOrAppNotFoundException) e.actualException;
} else if (e.actualException instanceof UnknownUserIdException) {
throw (UnknownUserIdException) e.actualException;
} else if (e.actualException instanceof AnotherPrimaryUserWithPhoneNumberAlreadyExistsException) {
throw (AnotherPrimaryUserWithPhoneNumberAlreadyExistsException) e.actualException;
} else if (e.actualException instanceof AnotherPrimaryUserWithEmailAlreadyExistsException) {
throw (AnotherPrimaryUserWithEmailAlreadyExistsException) e.actualException;
} else if (e.actualException instanceof AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException) {
throw (AnotherPrimaryUserWithThirdPartyInfoAlreadyExistsException) e.actualException;
}
throw new StorageQueryException(e.actualException);
}
}

@TestOnly
public static boolean removeUserIdFromTenant(Main main, TenantIdentifierWithStorage tenantIdentifierWithStorage,
String userId, String externalUserId)
throws FeatureNotEnabledException, TenantOrAppNotFoundException, StorageQueryException,
UnknownUserIdException {
try {
return removeUserIdFromTenant(main, tenantIdentifierWithStorage, userId, externalUserId, false);
} catch (DisassociationNotAllowedException e) {
throw new IllegalStateException("should never happen");
}
}

public static boolean removeUserIdFromTenant(Main main, TenantIdentifierWithStorage tenantIdentifierWithStorage,
String userId, String externalUserId, boolean disallowLastTenantDisassociation)
throws FeatureNotEnabledException, TenantOrAppNotFoundException, StorageQueryException,
UnknownUserIdException, DisassociationNotAllowedException {
if (Arrays.stream(FeatureFlag.getInstance(main, new AppIdentifier(null, null)).getEnabledFeatures())
.noneMatch(ee_features -> ee_features == EE_FEATURES.MULTI_TENANCY)) {
throw new FeatureNotEnabledException(EE_FEATURES.MULTI_TENANCY);
}

if (disallowLastTenantDisassociation) {
AuthRecipeUserInfo userInfo = AuthRecipe.getUserById(tenantIdentifierWithStorage.toAppIdentifierWithStorage(), userId);
if (userInfo != null) {
for (LoginMethod lM : userInfo.loginMethods) {
if (lM.getSupertokensUserId().equals(userId)) {
if (lM.tenantIds.size() == 1 && lM.tenantIds.contains(tenantIdentifierWithStorage.getTenantId())) {
throw new DisassociationNotAllowedException();
}
}
}
}
}

boolean finalDidExist = false;
boolean didExist = AuthRecipe.deleteNonAuthRecipeUser(tenantIdentifierWithStorage,
externalUserId == null ? userId : externalUserId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 DisassociationNotAllowedException extends Exception {
public DisassociationNotAllowedException() {
super("Disassociation not allowed");
}
}
Loading

0 comments on commit 3e39d0b

Please sign in to comment.