Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fixes storage handling for non-auth recipes #942

Merged
merged 31 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
eb4496b
fix: non auth recipe stuff
sattvikc Feb 29, 2024
d267312
fix: user roles
sattvikc Feb 29, 2024
6b2a45c
fix: half done
sattvikc Mar 1, 2024
fadf205
fix: thirdparty changes
sattvikc Mar 1, 2024
9a0ff85
fix: passwordless changes
sattvikc Mar 1, 2024
89fd936
fix: active users
sattvikc Mar 1, 2024
458c3b6
fix: session changes
sattvikc Mar 1, 2024
9eb76a1
fix: user metadata
sattvikc Mar 1, 2024
4843083
fix: user roles
sattvikc Mar 1, 2024
69a2466
fix: totp
sattvikc Mar 1, 2024
6728665
fix: email verification
sattvikc Mar 1, 2024
c61c7d7
fix: multitenancy and other minor fixes
sattvikc Mar 1, 2024
c1edaba
fix: compile errors
sattvikc Mar 1, 2024
dd688da
fix: bugs and tests
sattvikc Mar 1, 2024
c5fc6a3
fix: bugs and tests
sattvikc Mar 1, 2024
5f00b5e
fix: func rename
sattvikc Mar 1, 2024
311b9b0
fix: PR comments
sattvikc Mar 4, 2024
75b5a14
fix: pr comments
sattvikc Mar 4, 2024
38c11fd
fix: pr comments
sattvikc Mar 4, 2024
06569c0
fix: pr comments
sattvikc Mar 4, 2024
d94a381
fix: user role multitenant tests
sattvikc Mar 4, 2024
83b802c
fix: email verification tests
sattvikc Mar 4, 2024
3d93ab5
fix: user role deletion
sattvikc Mar 4, 2024
d7cbcfa
fix: user roles
sattvikc Mar 4, 2024
fb2234c
fix: user roles
sattvikc Mar 4, 2024
8945be1
fix: get tenant identifier refactor
sattvikc Mar 4, 2024
a5d7aad
fix: pr comments
sattvikc Mar 4, 2024
88539e5
fix: query
sattvikc Mar 4, 2024
8a31166
fix: tests version and changelog
sattvikc Mar 5, 2024
644b5d6
Update CHANGELOG.md
sattvikc Mar 5, 2024
0bfad8b
fix: pr comments
sattvikc Mar 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ee/src/main/java/io/supertokens/ee/EEFeatureFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ private JsonObject getMultiTenancyStats()
return stats;
}

private JsonObject getAccountLinkingStats() throws StorageQueryException {
private JsonObject getAccountLinkingStats() throws StorageQueryException, TenantOrAppNotFoundException {
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
JsonObject result = new JsonObject();
Storage[] storages = StorageLayer.getStoragesForApp(main, this.appIdentifier);
boolean usesAccountLinking = false;
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/io/supertokens/authRecipe/AuthRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorages;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
Expand Down Expand Up @@ -661,20 +662,20 @@ public static long getUsersCountForTenant(TenantIdentifierWithStorage tenantIden
tenantIdentifier, includeRecipeIds);
}

public static long getUsersCountAcrossAllTenants(AppIdentifierWithStorage appIdentifierWithStorage,
public static long getUsersCountAcrossAllTenants(AppIdentifierWithStorages appIdentifierWithStorages,
RECIPE_ID[] includeRecipeIds)
throws StorageQueryException,
TenantOrAppNotFoundException, BadPermissionException {
long count = 0;

for (Storage storage : appIdentifierWithStorage.getStorages()) {
for (Storage storage : appIdentifierWithStorages.getStorages()) {
if (storage.getType() != STORAGE_TYPE.SQL) {
// we only support SQL for now
throw new UnsupportedOperationException("");
}

count += ((AuthRecipeStorage) storage).getUsersCount(
appIdentifierWithStorage, includeRecipeIds);
appIdentifierWithStorages, includeRecipeIds);
}

return count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorages;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.storageLayer.StorageLayer;
Expand Down Expand Up @@ -102,13 +103,12 @@ protected void doTaskPerApp(AppIdentifier app) throws Exception {
if (StorageLayer.getBaseStorage(main).getType() == STORAGE_TYPE.SQL) {
{ // Users count across all tenants
Storage[] storages = StorageLayer.getStoragesForApp(main, app);
AppIdentifierWithStorage appIdentifierWithAllTenantStorages = new AppIdentifierWithStorage(
app.getConnectionUriDomain(), app.getAppId(),
StorageLayer.getStorage(app.getAsPublicTenantIdentifier(), main), storages
AppIdentifierWithStorages appIdentifierWithStorages = new AppIdentifierWithStorages(
app.getConnectionUriDomain(), app.getAppId(), storages
);

json.addProperty("usersCount",
AuthRecipe.getUsersCountAcrossAllTenants(appIdentifierWithAllTenantStorages, null));
AuthRecipe.getUsersCountAcrossAllTenants(appIdentifierWithStorages, null));
}

{ // Dashboard user emails
Expand Down
63 changes: 23 additions & 40 deletions src/main/java/io/supertokens/storageLayer/StorageLayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,8 @@ public static List<List<TenantIdentifier>> getTenantsWithUniqueUserPoolId(Main m
return result;
}

rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
public static Storage[] getStoragesForApp(Main main, AppIdentifier appIdentifier) {
public static Storage[] getStoragesForApp(Main main, AppIdentifier appIdentifier)
throws TenantOrAppNotFoundException {
Map<String, Storage> userPoolToStorage = new HashMap<>();

Map<ResourceDistributor.KeyClass, ResourceDistributor.SingletonResource> resources =
Expand All @@ -397,7 +398,11 @@ public static Storage[] getStoragesForApp(Main main, AppIdentifier appIdentifier
userPoolToStorage.put(storage.getUserPoolId(), storage);
}
}
return userPoolToStorage.values().toArray(new Storage[0]);
Storage[] storages = userPoolToStorage.values().toArray(new Storage[0]);
if (storages.length == 0) {
throw new TenantOrAppNotFoundException(appIdentifier);
}
return storages;
}

public static TenantIdentifierWithStorageAndUserIdMapping getTenantIdentifierWithStorageAndUserIdMappingForUser(
Expand Down Expand Up @@ -432,64 +437,42 @@ public static TenantIdentifierWithStorageAndUserIdMapping getTenantIdentifierWit
throw new UnknownUserIdException();
}

public static AppIdentifierWithStorageAndUserIdMapping getAppIdentifierWithStorageAndUserIdMappingForUserWithPriorityForTenantStorage(
Main main, AppIdentifier appIdentifier, Storage priorityStorage, String userId,
public static AppIdentifierWithStorageAndUserIdMapping getAppIdentifierWithStorageAndUserIdMappingForUser(
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
AppIdentifierWithStorages appIdentifierWithStorages, String userId,
UserIdType userIdType) throws StorageQueryException, TenantOrAppNotFoundException, UnknownUserIdException {

Storage[] storages = getStoragesForApp(main, appIdentifier);
Storage[] storages = appIdentifierWithStorages.getStorages();

if (storages.length == 0) {
throw new TenantOrAppNotFoundException(appIdentifier);
throw new TenantOrAppNotFoundException(appIdentifierWithStorages);
}

// We look for userId in the priorityStorage first just in case multiple storages have the mapping, we
// return the mapping from the storage of the tenant from which the request came from.
{
UserIdMapping mapping = io.supertokens.useridmapping.UserIdMapping.getUserIdMapping(
appIdentifier.withStorage(priorityStorage),
userId, userIdType);

if (mapping != null) {
AppIdentifierWithStorage appIdentifierWithStorage = appIdentifier.withStorage(priorityStorage);
return new AppIdentifierWithStorageAndUserIdMapping(appIdentifierWithStorage, mapping);
}
AppIdentifier appIdentifier = appIdentifierWithStorages;

// First look in auth recipes
for (Storage storage : storages) {
if (userIdType != UserIdType.EXTERNAL
&& ((AuthRecipeStorage) priorityStorage).doesUserIdExist(appIdentifier, userId)) {
AppIdentifierWithStorage appIdentifierWithStorage = appIdentifier.withStorage(priorityStorage);
return new AppIdentifierWithStorageAndUserIdMapping(appIdentifierWithStorage, null);
}
if (userIdType != UserIdType.SUPERTOKENS) {
AppIdentifierWithStorage appIdentifierWithStorage = appIdentifier.withStorage(priorityStorage);
try {
io.supertokens.useridmapping.UserIdMapping.findNonAuthStoragesWhereUserIdIsUsedOrAssertIfUsed(
appIdentifierWithStorage, userId, true);
} catch (ServletException e) {
// this means that the userId is being used for a non auth recipe.
return new AppIdentifierWithStorageAndUserIdMapping(appIdentifierWithStorage, null);
}
&& ((AuthRecipeStorage) storage).doesUserIdExist(appIdentifier, userId)) {
AppIdentifierWithStorage appIdentifierWithStorage = appIdentifier.withStorage(storage);

UserIdMapping mapping = io.supertokens.useridmapping.UserIdMapping.getUserIdMapping(
appIdentifierWithStorages.withStorage(storage),
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
userId, userIdType);

return new AppIdentifierWithStorageAndUserIdMapping(appIdentifierWithStorage, mapping);
}
}

for (Storage storage : storages) {
if (storage == priorityStorage) {
continue; // Already checked previously
}

UserIdMapping mapping = io.supertokens.useridmapping.UserIdMapping.getUserIdMapping(
appIdentifier.withStorage(storage),
appIdentifierWithStorages.withStorage(storage),
userId, userIdType);

if (mapping != null) {
AppIdentifierWithStorage appIdentifierWithStorage = appIdentifier.withStorage(storage);
return new AppIdentifierWithStorageAndUserIdMapping(appIdentifierWithStorage, mapping);
}

if (userIdType != UserIdType.EXTERNAL
&& ((AuthRecipeStorage) storage).doesUserIdExist(appIdentifier, userId)) {
AppIdentifierWithStorage appIdentifierWithStorage = appIdentifier.withStorage(storage);
return new AppIdentifierWithStorageAndUserIdMapping(appIdentifierWithStorage, null);
}
if (userIdType != UserIdType.SUPERTOKENS) {
AppIdentifierWithStorage appIdentifierWithStorage = appIdentifier.withStorage(storage);
try {
Expand Down
45 changes: 31 additions & 14 deletions src/main/java/io/supertokens/useridmapping/UserIdMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.jwt.JWTRecipeStorage;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorages;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.session.SessionStorage;
Expand All @@ -50,16 +51,26 @@
public class UserIdMapping {

@TestOnly
public static void createUserIdMapping(Main main, AppIdentifierWithStorage appIdentifierWithStorage,
public static void createUserIdMapping(AppIdentifierWithStorages appIdentifierWithStorages,
String superTokensUserId, String externalUserId,
String externalUserIdInfo, boolean force)
throws ServletException, UnknownSuperTokensUserIdException, UserIdMappingAlreadyExistsException,
StorageQueryException, TenantOrAppNotFoundException {
createUserIdMapping(main, appIdentifierWithStorage, superTokensUserId, externalUserId, externalUserIdInfo,
createUserIdMapping(appIdentifierWithStorages, superTokensUserId, externalUserId, externalUserIdInfo,
force, false);
}

public static void createUserIdMapping(Main main, AppIdentifierWithStorage appIdentifierWithStorage,
@TestOnly
public static void createUserIdMapping(Main main, AppIdentifierWithStorage appIdentifierWithStorage, String supertokensUserId, String externalUserId, String externalUserIdInfo, boolean force)
throws ServletException, UnknownSuperTokensUserIdException, UserIdMappingAlreadyExistsException,
StorageQueryException, TenantOrAppNotFoundException {
createUserIdMapping(
new AppIdentifierWithStorages(appIdentifierWithStorage.getConnectionUriDomain(), appIdentifierWithStorage.getAppId(), new Storage[]{appIdentifierWithStorage.getStorage()}),
supertokensUserId, externalUserId, externalUserIdInfo, force
);
}

public static void createUserIdMapping(AppIdentifierWithStorages appIdentifierWithStorages,
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
String superTokensUserId, String externalUserId,
String externalUserIdInfo, boolean force, boolean makeExceptionForEmailVerification)
throws UnknownSuperTokensUserIdException,
Expand All @@ -75,9 +86,8 @@ public static void createUserIdMapping(Main main, AppIdentifierWithStorage appId
// race condition is fixed.
try { // with external id
AppIdentifierWithStorageAndUserIdMapping mappingAndStorage =
StorageLayer.getAppIdentifierWithStorageAndUserIdMappingForUserWithPriorityForTenantStorage(
main, appIdentifierWithStorage, appIdentifierWithStorage.getStorage(), externalUserId,
UserIdType.EXTERNAL);
StorageLayer.getAppIdentifierWithStorageAndUserIdMappingForUser(
appIdentifierWithStorages, externalUserId, UserIdType.EXTERNAL);

if (mappingAndStorage.userIdMapping != null) {
throw new UserIdMappingAlreadyExistsException(
Expand All @@ -89,6 +99,16 @@ public static void createUserIdMapping(Main main, AppIdentifierWithStorage appId
// ignore this as we do not want external user id to exist
}

AppIdentifierWithStorageAndUserIdMapping mappingAndStorage;
try {
mappingAndStorage = StorageLayer.getAppIdentifierWithStorageAndUserIdMappingForUser(
appIdentifierWithStorages, superTokensUserId, UserIdType.SUPERTOKENS);
} catch (UnknownUserIdException e) {
throw new UnknownSuperTokensUserIdException();
}

AppIdentifierWithStorage appIdentifierWithStorage = mappingAndStorage.appIdentifierWithStorage;

// if a userIdMapping is created with force, then we skip the following checks
if (!force) {
// We do not allow for a UserIdMapping to be created when the externalUserId is a SuperTokens userId.
Expand All @@ -99,7 +119,7 @@ public static void createUserIdMapping(Main main, AppIdentifierWithStorage appId

{
if (((AuthRecipeStorage) appIdentifierWithStorage.getStorage()).doesUserIdExist(
appIdentifierWithStorage, externalUserId)) {
appIdentifierWithStorages, externalUserId)) {
throw new ServletException(new WebserverAPI.BadRequestException(
"Cannot create a userId mapping where the externalId is also a SuperTokens userID"));
}
Expand All @@ -115,7 +135,7 @@ public static void createUserIdMapping(Main main, AppIdentifierWithStorage appId
// an exception, then the creation of userIdMapping for the user will be blocked. And, to overcome that the
// email will have to be unverified first, then the userIdMapping should be created and then the email must be
// verified again on the externalUserId, which is not a good user experience.
appIdentifierWithStorage.getEmailVerificationStorage().updateIsEmailVerifiedToExternalUserId(appIdentifierWithStorage, superTokensUserId, externalUserId);
appIdentifierWithStorage.getEmailVerificationStorage().updateIsEmailVerifiedToExternalUserId(appIdentifierWithStorages, superTokensUserId, externalUserId);
} else if (storageClasses.size() > 0) {
String recipeName = storageClasses.get(0);
String[] parts = recipeName.split("[.]");
Expand All @@ -127,12 +147,10 @@ public static void createUserIdMapping(Main main, AppIdentifierWithStorage appId
} else {
findNonAuthStoragesWhereUserIdIsUsedOrAssertIfUsed(appIdentifierWithStorage, superTokensUserId, true);
}


}

appIdentifierWithStorage.getUserIdMappingStorage()
.createUserIdMapping(appIdentifierWithStorage, superTokensUserId,
.createUserIdMapping(appIdentifierWithStorages, superTokensUserId,
externalUserId, externalUserIdInfo);
}
@TestOnly
Expand All @@ -152,9 +170,8 @@ public static void createUserIdMapping(Main main,
UserIdMappingAlreadyExistsException, StorageQueryException, ServletException, UnknownUserIdException {
try {
Storage storage = StorageLayer.getStorage(main);
createUserIdMapping(main, new AppIdentifierWithStorage(null, null, storage), superTokensUserId,
externalUserId,
externalUserIdInfo, force, makeExceptionForEmailVerification);
createUserIdMapping(new AppIdentifierWithStorages(null, null, new Storage[]{storage}), superTokensUserId,
externalUserId, externalUserIdInfo, force, makeExceptionForEmailVerification);
} catch (TenantOrAppNotFoundException e) {
throw new IllegalStateException(e);
}
Expand Down
Loading
Loading