Skip to content

Commit

Permalink
fix: test email verified with user id mapping (#873)
Browse files Browse the repository at this point in the history
* fix: email verified in login methods

* fix: test

* fix: version

* fix: ev fix for inmemory
  • Loading branch information
sattvikc authored Nov 1, 2023
1 parent 7ec4fee commit 9a8a366
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [7.0.9] - 2023-11-01

- Tests `verified` in `loginMethods` for users with userId mapping

## [7.0.8] - 2023-10-19

- Tests thirdParty serialization fix
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
// }
//}

version = "7.0.8"
version = "7.0.9"


repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,26 +243,50 @@ public static List<String> isEmailVerified_transaction(Start start, Connection s
return new ArrayList<>();
}
List<String> emails = new ArrayList<>();
List<String> userIds = new ArrayList<>();
Map<String, String> userIdToEmailMap = new HashMap<>();
List<String> supertokensUserIds = new ArrayList<>();
for (UserIdAndEmail ue : userIdAndEmail) {
emails.add(ue.email);
userIds.add(ue.userId);
supertokensUserIds.add(ue.userId);
}

// We have external user id stored in the email verification table, so we need to fetch the mapped userids for
// calculating the verified emails

HashMap<String, String> supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds_Transaction(start,
sqlCon, supertokensUserIds);
HashMap<String, String> externalUserIdToSupertokensUserIdMap = new HashMap<>();

List<String> supertokensOrExternalUserIdsToQuery = new ArrayList<>();
for (String userId : supertokensUserIds) {
if (supertokensUserIdToExternalUserIdMap.containsKey(userId)) {
supertokensOrExternalUserIdsToQuery.add(supertokensUserIdToExternalUserIdMap.get(userId));
externalUserIdToSupertokensUserIdMap.put(supertokensUserIdToExternalUserIdMap.get(userId), userId);
} else {
supertokensOrExternalUserIdsToQuery.add(userId);
externalUserIdToSupertokensUserIdMap.put(userId, userId);
}
}

Map<String, String> supertokensOrExternalUserIdToEmailMap = new HashMap<>();
for (UserIdAndEmail ue : userIdAndEmail) {
if (userIdToEmailMap.containsKey(ue.userId)) {
String supertokensOrExternalUserId = ue.userId;
if (supertokensUserIdToExternalUserIdMap.containsKey(supertokensOrExternalUserId)) {
supertokensOrExternalUserId = supertokensUserIdToExternalUserIdMap.get(supertokensOrExternalUserId);
}
if (supertokensOrExternalUserIdToEmailMap.containsKey(supertokensOrExternalUserId)) {
throw new RuntimeException("Found a bug!");
}
userIdToEmailMap.put(ue.userId, ue.email);
supertokensOrExternalUserIdToEmailMap.put(supertokensOrExternalUserId, ue.email);
}

String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable()
+ " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
+ " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) +
") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")";

return execute(sqlCon, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
int index = 2;
for (String userId : userIds) {
for (String userId : supertokensOrExternalUserIdsToQuery) {
pst.setString(index++, userId);
}
for (String email : emails) {
Expand All @@ -271,10 +295,10 @@ public static List<String> isEmailVerified_transaction(Start start, Connection s
}, result -> {
List<String> res = new ArrayList<>();
while (result.next()) {
String userId = result.getString("user_id");
String supertokensOrExternalUserId = result.getString("user_id");
String email = result.getString("email");
if (Objects.equals(userIdToEmailMap.get(userId), email)) {
res.add(userId);
if (Objects.equals(supertokensOrExternalUserIdToEmailMap.get(supertokensOrExternalUserId), email)) {
res.add(externalUserIdToSupertokensUserIdMap.get(supertokensOrExternalUserId));
}
}
return res;
Expand All @@ -288,26 +312,46 @@ public static List<String> isEmailVerified(Start start, AppIdentifier appIdentif
return new ArrayList<>();
}
List<String> emails = new ArrayList<>();
List<String> userIds = new ArrayList<>();
Map<String, String> userIdToEmailMap = new HashMap<>();
List<String> supertokensUserIds = new ArrayList<>();

for (UserIdAndEmail ue : userIdAndEmail) {
emails.add(ue.email);
userIds.add(ue.userId);
supertokensUserIds.add(ue.userId);
}
// We have external user id stored in the email verification table, so we need to fetch the mapped userids for
// calculating the verified emails
HashMap<String, String> supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds(start,
supertokensUserIds);
HashMap<String, String> externalUserIdToSupertokensUserIdMap = new HashMap<>();
List<String> supertokensOrExternalUserIdsToQuery = new ArrayList<>();
for (String userId : supertokensUserIds) {
if (supertokensUserIdToExternalUserIdMap.containsKey(userId)) {
supertokensOrExternalUserIdsToQuery.add(supertokensUserIdToExternalUserIdMap.get(userId));
externalUserIdToSupertokensUserIdMap.put(supertokensUserIdToExternalUserIdMap.get(userId), userId);
} else {
supertokensOrExternalUserIdsToQuery.add(userId);
externalUserIdToSupertokensUserIdMap.put(userId, userId);
}
}

Map<String, String> supertokensOrExternalUserIdToEmailMap = new HashMap<>();
for (UserIdAndEmail ue : userIdAndEmail) {
if (userIdToEmailMap.containsKey(ue.userId)) {
String supertokensOrExternalUserId = ue.userId;
if (supertokensUserIdToExternalUserIdMap.containsKey(supertokensOrExternalUserId)) {
supertokensOrExternalUserId = supertokensUserIdToExternalUserIdMap.get(supertokensOrExternalUserId);
}
if (supertokensOrExternalUserIdToEmailMap.containsKey(supertokensOrExternalUserId)) {
throw new RuntimeException("Found a bug!");
}
userIdToEmailMap.put(ue.userId, ue.email);
supertokensOrExternalUserIdToEmailMap.put(supertokensOrExternalUserId, ue.email);
}
String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable()
+ " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
+ " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) +
") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")";

return execute(start, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
int index = 2;
for (String userId : userIds) {
for (String userId : supertokensOrExternalUserIdsToQuery) {
pst.setString(index++, userId);
}
for (String email : emails) {
Expand All @@ -316,10 +360,10 @@ public static List<String> isEmailVerified(Start start, AppIdentifier appIdentif
}, result -> {
List<String> res = new ArrayList<>();
while (result.next()) {
String userId = result.getString("user_id");
String supertokensOrExternalUserId = result.getString("user_id");
String email = result.getString("email");
if (Objects.equals(userIdToEmailMap.get(userId), email)) {
res.add(userId);
if (Objects.equals(supertokensOrExternalUserIdToEmailMap.get(supertokensOrExternalUserId), email)) {
res.add(externalUserIdToSupertokensUserIdMap.get(supertokensOrExternalUserId));
}
}
return res;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static io.supertokens.inmemorydb.QueryExecutorTemplate.execute;
import static io.supertokens.inmemorydb.QueryExecutorTemplate.update;
Expand Down Expand Up @@ -135,7 +136,7 @@ public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExter

}

public static HashMap<String, String> getUserIdMappingWithUserIds(Start start, ArrayList<String> userIds)
public static HashMap<String, String> getUserIdMappingWithUserIds(Start start, List<String> userIds)
throws SQLException, StorageQueryException {

if (userIds.size() == 0) {
Expand Down Expand Up @@ -168,6 +169,39 @@ public static HashMap<String, String> getUserIdMappingWithUserIds(Start start, A
});
}

public static HashMap<String, String> getUserIdMappingWithUserIds_Transaction(Start start, Connection sqlCon, List<String> userIds)
throws SQLException, StorageQueryException {

if (userIds.size() == 0) {
return new HashMap<>();
}

// No need to filter based on tenantId because the id list is already filtered for a tenant
StringBuilder QUERY = new StringBuilder(
"SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE supertokens_user_id IN (");
for (int i = 0; i < userIds.size(); i++) {
QUERY.append("?");
if (i != userIds.size() - 1) {
// not the last element
QUERY.append(",");
}
}
QUERY.append(")");
return execute(sqlCon, QUERY.toString(), pst -> {
for (int i = 0; i < userIds.size(); i++) {
// i+1 cause this starts with 1 and not 0
pst.setString(i + 1, userIds.get(i));
}
}, result -> {
HashMap<String, String> userIdMappings = new HashMap<>();
while (result.next()) {
UserIdMapping temp = UserIdMappingRowMapper.getInstance().mapOrThrow(result);
userIdMappings.put(temp.superTokensUserId, temp.externalUserId);
}
return userIdMappings;
});
}

public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId)
throws SQLException, StorageQueryException {
String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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.test.accountlinking.api;

import com.google.gson.JsonObject;
import io.supertokens.ProcessState;
import io.supertokens.authRecipe.AuthRecipe;
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailverification.EmailVerification;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.test.TestingProcessManager;
import io.supertokens.test.Utils;
import io.supertokens.test.httpRequest.HttpRequestForTesting;
import io.supertokens.useridmapping.UserIdMapping;
import io.supertokens.webserver.WebserverAPI;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;

import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.*;

public class EmailVerificationTest {
@Rule
public TestRule watchman = Utils.getOnFailure();

@AfterClass
public static void afterTesting() {
Utils.afterTesting();
}

@Before
public void beforeEach() {
Utils.reset();
}

@Test
public void testEmailVerificationWithUserIdMapping() throws Exception {
String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
return;
}

AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "[email protected]", "password");
UserIdMapping.createUserIdMapping(process.getProcess(), user1.getSupertokensUserId(), "euserid1", null, false);
String token = EmailVerification.generateEmailVerificationToken(process.getProcess(), "euserid1", "[email protected]");
EmailVerification.verifyEmail(process.getProcess(), token);

AuthRecipeUserInfo user2 = EmailPassword.signUp(process.getProcess(), "[email protected]", "password");
UserIdMapping.createUserIdMapping(process.getProcess(), user2.getSupertokensUserId(), "euserid2", null, false);

AuthRecipe.createPrimaryUser(process.getProcess(), user1.getSupertokensUserId());
AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), user1.getSupertokensUserId());

{
Map<String, String> params = new HashMap<>();
params.put("userId", "euserid1");
JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/user/id", params, 1000, 1000, null,
WebserverAPI.getLatestCDIVersion().get(), "");
JsonObject user = response.get("user").getAsJsonObject();
assertTrue(user.get("loginMethods").getAsJsonArray().get(0).getAsJsonObject().get("verified").getAsBoolean());
assertFalse(user.get("loginMethods").getAsJsonArray().get(1).getAsJsonObject().get("verified").getAsBoolean());
}

process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}
}

0 comments on commit 9a8a366

Please sign in to comment.