From 6b2657cc24611e1c356b61138f7c0587fc6ea47a Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Thu, 16 Nov 2023 16:53:31 +0530 Subject: [PATCH 1/3] feat: normalise phoneNumber received in the input before processing --- build.gradle | 3 ++ implementationDependencies.json | 5 ++ src/main/java/io/supertokens/utils/Utils.java | 54 +++++++++++++++++-- .../api/core/ListUsersByAccountInfoAPI.java | 3 +- .../api/passwordless/CreateCodeAPI.java | 9 ++-- .../api/passwordless/DeleteCodesAPI.java | 4 +- .../api/passwordless/GetCodesAPI.java | 2 +- .../webserver/api/passwordless/UserAPI.java | 4 +- .../java/io/supertokens/test/UtilsTest.java | 49 +++++++++++++++-- .../api/GetUserByAccountInfoTest.java | 27 ++++++++++ .../PasswordlessCreateCodeAPITest2_11.java | 45 ++++++++++++++++ .../PasswordlessDeleteCodesAPITest2_11.java | 41 ++++++++++++++ .../api/PasswordlessGetCodesAPITest2_11.java | 42 +++++++++++++++ .../api/PasswordlessUserGetAPITest2_11.java | 38 ++++++++++++- .../api/PasswordlessUserPutAPITest2_11.java | 40 ++++++++++++++ 15 files changed, 346 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 528c275fb..0aa76ece0 100644 --- a/build.gradle +++ b/build.gradle @@ -71,6 +71,9 @@ dependencies { // https://mvnrepository.com/artifact/commons-codec/commons-codec implementation group: 'commons-codec', name: 'commons-codec', version: '1.15' + // https://mvnrepository.com/artifact/com.googlecode.libphonenumber/libphonenumber/ + implementation group: 'com.googlecode.libphonenumber', name: 'libphonenumber', version: '8.13.12' + compileOnly project(":supertokens-plugin-interface") testImplementation project(":supertokens-plugin-interface") diff --git a/implementationDependencies.json b/implementationDependencies.json index 1fdb8e150..e0ab94e68 100644 --- a/implementationDependencies.json +++ b/implementationDependencies.json @@ -110,6 +110,11 @@ "jar": "https://repo1.maven.org/maven2/commons-codec/commons-codec/1.15/commons-codec-1.15.jar", "name": "Commons Codec 1.15", "src": "https://repo1.maven.org/maven2/commons-codec/commons-codec/1.15/commons-codec-1.15-sources.jar" + }, + { + "jar": "https://repo1.maven.org/maven2/com/googlecode/libphonenumber/libphonenumber/8.13.25/libphonenumber-8.13.25.jar", + "name": "Libphonenumber 8.13.25", + "src": "https://repo1.maven.org/maven2/com/googlecode/libphonenumber/libphonenumber/8.13.25/libphonenumber-8.13.25-sources.jar" } ] } \ No newline at end of file diff --git a/src/main/java/io/supertokens/utils/Utils.java b/src/main/java/io/supertokens/utils/Utils.java index 37b5a9e98..ca66eaa63 100644 --- a/src/main/java/io/supertokens/utils/Utils.java +++ b/src/main/java/io/supertokens/utils/Utils.java @@ -20,6 +20,9 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.Phonenumber; import io.supertokens.Main; import io.supertokens.config.Config; import io.supertokens.jwt.exceptions.UnsupportedJWTSigningAlgorithmException; @@ -55,6 +58,42 @@ public class Utils { + /** + * Normalizes a phone number by trimming and formatting it according to the + * E.164 standard. + *

+ * The function attempts to parse the given phone number using libphonenumber. + * If parsing is successful, + * it formats the phone number according to the E.164 standard. If parsing fails + * (throws a NumberParseException), + * it still trims the input and returns the original trimmed phone number. + * + * @param phoneNumber The input phone number to be normalized. + * @return The normalized phone number or the original trimmed phone number if + * it cannot be parsed. + */ + public static String normalizeIfPhoneNumber(String phoneNumber) { + if (phoneNumber == null) { + return null; + } + + PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + + try { + // Attempt to parse the phone number with default region code "ZZ" (unknown + // region) + Phonenumber.PhoneNumber parsedPhoneNumber = phoneNumberUtil.parse(phoneNumber.trim(), "ZZ"); + + // Format the parsed phone number according to E.164 standard + phoneNumber = phoneNumberUtil.format(parsedPhoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); + } catch (NumberParseException e) { + // Parsing failed, use the original trimmed phone number + phoneNumber = phoneNumber.trim(); + } + + return phoneNumber; + } + public static String normaliseEmail(String email) { if (email == null) { return null; @@ -79,7 +118,8 @@ public static String convertToBase64(String str) { // This function deserializes both B64 and B64URL encodings public static String convertFromBase64(String str) { - return new String(Base64.getDecoder().decode(stringToBytes(str.replace("-", "+").replace("_", "/"))), StandardCharsets.UTF_8); + return new String(Base64.getDecoder().decode(stringToBytes(str.replace("-", "+").replace("_", "/"))), + StandardCharsets.UTF_8); } public static String throwableStacktraceToString(Throwable e) { @@ -282,10 +322,13 @@ public static class PubPriKey { } public PubPriKey(String s) { - // We split by both | and ; because in old versions we used to use ";" in dynamic and "|" in static keys - // Now we are consolidating all of them to use "|", but by handling legacy keys, we can avoid the need + // We split by both | and ; because in old versions we used to use ";" in + // dynamic and "|" in static keys + // Now we are consolidating all of them to use "|", but by handling legacy keys, + // we can avoid the need // for manual key migration. - // I.e.: this way only people who set access_token_signing_key_dynamic to false has to do manual + // I.e.: this way only people who set access_token_signing_key_dynamic to false + // has to do manual // migration instead of everyone. // for everyone else, the key rotation should get it done. String[] parts = s.split("[|;]"); @@ -338,7 +381,8 @@ public static JsonObject addLegacySigningKeyInfos(AppIdentifier appIdentifier, M TenantOrAppNotFoundException { if (Config.getConfig(appIdentifier.getAsPublicTenantIdentifier(), main).getAccessTokenSigningKeyDynamic()) { result.addProperty("jwtSigningPublicKey", - new Utils.PubPriKey(SigningKeys.getInstance(appIdentifier, main).getLatestIssuedDynamicKey().value).publicKey); + new Utils.PubPriKey( + SigningKeys.getInstance(appIdentifier, main).getLatestIssuedDynamicKey().value).publicKey); result.addProperty("jwtSigningPublicKeyExpiryTime", SigningKeys.getInstance(appIdentifier, main).getDynamicSigningKeyExpiryTime()); diff --git a/src/main/java/io/supertokens/webserver/api/core/ListUsersByAccountInfoAPI.java b/src/main/java/io/supertokens/webserver/api/core/ListUsersByAccountInfoAPI.java index 2cc2a7eb5..9cb27d0a8 100644 --- a/src/main/java/io/supertokens/webserver/api/core/ListUsersByAccountInfoAPI.java +++ b/src/main/java/io/supertokens/webserver/api/core/ListUsersByAccountInfoAPI.java @@ -50,7 +50,8 @@ public String getPath() { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { // API is tenant specific. String email = InputParser.getQueryParamOrThrowError(req, "email", true); - String phoneNumber = InputParser.getQueryParamOrThrowError(req, "phoneNumber", true); + String phoneNumber = Utils.normalizeIfPhoneNumber( + InputParser.getQueryParamOrThrowError(req, "phoneNumber", true)); String thirdPartyId = InputParser.getQueryParamOrThrowError(req, "thirdPartyId", true); String thirdPartyUserId = InputParser.getQueryParamOrThrowError(req, "thirdPartyUserId", true); diff --git a/src/main/java/io/supertokens/webserver/api/passwordless/CreateCodeAPI.java b/src/main/java/io/supertokens/webserver/api/passwordless/CreateCodeAPI.java index aa6c1d47c..96ee64990 100644 --- a/src/main/java/io/supertokens/webserver/api/passwordless/CreateCodeAPI.java +++ b/src/main/java/io/supertokens/webserver/api/passwordless/CreateCodeAPI.java @@ -63,7 +63,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I String email = input.has("email") ? Utils.normaliseEmail(InputParser.parseStringOrThrowError(input, "email", false)) : null; - String phoneNumber = InputParser.parseStringOrThrowError(input, "phoneNumber", true); + String phoneNumber = Utils.normalizeIfPhoneNumber( + InputParser.parseStringOrThrowError(input, "phoneNumber", true)); String deviceId = InputParser.parseStringOrThrowError(input, "deviceId", true); if (Stream.of(email, phoneNumber, deviceId).filter(Objects::nonNull).count() != 1) { @@ -77,7 +78,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I } try { - CreateCodeResponse createCodeResponse = Passwordless.createCode(this.getTenantIdentifierWithStorageFromRequest(req), main, email, + CreateCodeResponse createCodeResponse = Passwordless.createCode( + this.getTenantIdentifierWithStorageFromRequest(req), main, email, phoneNumber, deviceId, userInputCode); long passwordlessCodeLifetime = Config.getConfig(this.getTenantIdentifierWithStorageFromRequest(req), main) @@ -104,7 +106,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I JsonObject result = new JsonObject(); result.addProperty("status", "USER_INPUT_CODE_ALREADY_USED_ERROR"); super.sendJsonResponse(200, result, resp); - } catch (StorageQueryException | NoSuchAlgorithmException | InvalidKeyException | TenantOrAppNotFoundException | BadPermissionException e) { + } catch (StorageQueryException | NoSuchAlgorithmException | InvalidKeyException | TenantOrAppNotFoundException | + BadPermissionException e) { throw new ServletException(e); } catch (Base64EncodingException ex) { throw new ServletException(new BadRequestException("Input encoding error in " + ex.source)); diff --git a/src/main/java/io/supertokens/webserver/api/passwordless/DeleteCodesAPI.java b/src/main/java/io/supertokens/webserver/api/passwordless/DeleteCodesAPI.java index 153356709..476e4b562 100644 --- a/src/main/java/io/supertokens/webserver/api/passwordless/DeleteCodesAPI.java +++ b/src/main/java/io/supertokens/webserver/api/passwordless/DeleteCodesAPI.java @@ -52,8 +52,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I JsonObject input = InputParser.parseJsonObjectOrThrowError(req); String email = InputParser.parseStringOrThrowError(input, "email", true); - String phoneNumber = InputParser.parseStringOrThrowError(input, "phoneNumber", true); - + String phoneNumber = Utils.normalizeIfPhoneNumber( + InputParser.parseStringOrThrowError(input, "phoneNumber", true)); if (phoneNumber != null && email != null) { throw new ServletException(new BadRequestException("Please provide exactly one of email or phoneNumber")); } diff --git a/src/main/java/io/supertokens/webserver/api/passwordless/GetCodesAPI.java b/src/main/java/io/supertokens/webserver/api/passwordless/GetCodesAPI.java index 0d2168dd4..4a318195c 100644 --- a/src/main/java/io/supertokens/webserver/api/passwordless/GetCodesAPI.java +++ b/src/main/java/io/supertokens/webserver/api/passwordless/GetCodesAPI.java @@ -60,7 +60,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IO // logic based on: https://app.code2flow.com/Odo88u7TNKIk String email = InputParser.getQueryParamOrThrowError(req, "email", true); - String phoneNumber = InputParser.getQueryParamOrThrowError(req, "phoneNumber", true); + String phoneNumber = Utils.normalizeIfPhoneNumber(InputParser.getQueryParamOrThrowError(req, "phoneNumber", true)); String deviceId = InputParser.getQueryParamOrThrowError(req, "deviceId", true); String deviceIdHash = InputParser.getQueryParamOrThrowError(req, "preAuthSessionId", true); diff --git a/src/main/java/io/supertokens/webserver/api/passwordless/UserAPI.java b/src/main/java/io/supertokens/webserver/api/passwordless/UserAPI.java index 253a3f81a..c4f3854f3 100644 --- a/src/main/java/io/supertokens/webserver/api/passwordless/UserAPI.java +++ b/src/main/java/io/supertokens/webserver/api/passwordless/UserAPI.java @@ -65,7 +65,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IO // logic based on: https://app.code2flow.com/flowcharts/617a9aafdc97ee415448db74 String userId = InputParser.getQueryParamOrThrowError(req, "userId", true); String email = InputParser.getQueryParamOrThrowError(req, "email", true); - String phoneNumber = InputParser.getQueryParamOrThrowError(req, "phoneNumber", true); + String phoneNumber = Utils.normalizeIfPhoneNumber(InputParser.getQueryParamOrThrowError(req, "phoneNumber", true)); if (Stream.of(userId, email, phoneNumber).filter(Objects::nonNull).count() != 1) { throw new ServletException( @@ -149,7 +149,7 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO FieldUpdate phoneNumberUpdate = !input.has("phoneNumber") ? null : new FieldUpdate(input.get("phoneNumber").isJsonNull() ? null - : InputParser.parseStringOrThrowError(input, "phoneNumber", false)); + : Utils.normalizeIfPhoneNumber(InputParser.parseStringOrThrowError(input, "phoneNumber", false))); try { AppIdentifierWithStorageAndUserIdMapping appIdentifierWithStorageAndUserIdMapping = diff --git a/src/test/java/io/supertokens/test/UtilsTest.java b/src/test/java/io/supertokens/test/UtilsTest.java index 6de5d830f..583cc7573 100644 --- a/src/test/java/io/supertokens/test/UtilsTest.java +++ b/src/test/java/io/supertokens/test/UtilsTest.java @@ -22,6 +22,8 @@ import org.junit.Test; import org.junit.rules.TestRule; +import static org.junit.Assert.*; + public class UtilsTest { @Rule public TestRule watchman = Utils.getOnFailure(); @@ -38,7 +40,8 @@ public void beforeEach() { @Test public void encodeDecodeBase64WithUTF() { - assert (io.supertokens.utils.Utils.convertFromBase64(io.supertokens.utils.Utils.convertToBase64("łukasz 馬 / 马")) + assert (io.supertokens.utils.Utils.convertFromBase64( + io.supertokens.utils.Utils.convertToBase64("łukasz 馬 / 马")) .equals("łukasz 馬 / 马")); } @@ -46,15 +49,51 @@ public void encodeDecodeBase64WithUTF() { public void pubPriKeyShouldHandleSemicolonSeparator() { io.supertokens.utils.Utils.PubPriKey parsed = new io.supertokens.utils.Utils.PubPriKey("pub;pri"); - assert ( parsed.privateKey.equals("pri")); - assert ( parsed.publicKey.equals("pub")); + assert (parsed.privateKey.equals("pri")); + assert (parsed.publicKey.equals("pub")); } @Test public void pubPriKeyShouldHandleBarSeparator() { io.supertokens.utils.Utils.PubPriKey parsed = new io.supertokens.utils.Utils.PubPriKey("pub|pri"); - assert ( parsed.privateKey.equals("pri")); - assert ( parsed.publicKey.equals("pub")); + assert (parsed.privateKey.equals("pri")); + assert (parsed.publicKey.equals("pub")); + } + + @Test + public void testNormalizeValidPhoneNumber() { + String inputPhoneNumber = "+1 650-555-1234"; + String expectedNormalizedPhoneNumber = "+16505551234"; + String actualNormalizedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber); + assertEquals(expectedNormalizedPhoneNumber, actualNormalizedPhoneNumber); + } + + @Test + public void testNormalizeInvalidPhoneNumber() { + String inputPhoneNumber = "ThisIsNotAPhoneNumber"; + String expectedTrimmedPhoneNumber = "ThisIsNotAPhoneNumber"; + String actualNormalizedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber); + assertEquals(expectedTrimmedPhoneNumber, actualNormalizedPhoneNumber); + } + + @Test + public void testNormalizeNullPhoneNumber() { + String inputPhoneNumber = null; + assertNull(io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber)); + } + + @Test + public void testNormalizeEmptyPhoneNumber() { + // Test with an empty input + String inputPhoneNumber = ""; + assertEquals("", io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber)); + } + + @Test + public void testNormalizeWhitespacePhoneNumber() { + // Test with a phone number containing only whitespace + String inputPhoneNumber = " "; + assertEquals("", io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber)); } } diff --git a/src/test/java/io/supertokens/test/accountlinking/api/GetUserByAccountInfoTest.java b/src/test/java/io/supertokens/test/accountlinking/api/GetUserByAccountInfoTest.java index b799ecbcf..fc775fec1 100644 --- a/src/test/java/io/supertokens/test/accountlinking/api/GetUserByAccountInfoTest.java +++ b/src/test/java/io/supertokens/test/accountlinking/api/GetUserByAccountInfoTest.java @@ -173,6 +173,33 @@ public void testListUsersByAccountInfoForUnlinkedAccounts() throws Exception { assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + @Test + public void testListUserByAccountInfoByUnnormalisedPhoneNumber() 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; + } + + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + + AuthRecipeUserInfo user = createPasswordlessUserWithPhone(process.getProcess(), normalisedPhoneNumber); + + JsonObject userJSON = getUserById(process.getProcess(), user.getSupertokensUserId()); + + assertEquals(userJSON, getUsersByAccountInfo(process.getProcess(), false, null, phoneNumber, null, null).get(0)); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + @Test public void testListUsersByAccountInfoForUnlinkedAccountsWithUnionOption() throws Exception { String[] args = {"../"}; diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java index 596382a61..5561d50e7 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java @@ -16,6 +16,7 @@ package io.supertokens.test.passwordless.api; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.supertokens.ProcessState; import io.supertokens.pluginInterface.STORAGE_TYPE; @@ -26,6 +27,8 @@ import io.supertokens.test.httpRequest.HttpResponseException; import io.supertokens.utils.SemVer; +import io.supertokens.webserver.WebserverAPI; + import org.junit.AfterClass; import org.junit.Before; import org.junit.Rule; @@ -35,6 +38,7 @@ import static org.junit.Assert.*; import java.util.Base64; +import java.util.HashMap; import java.util.UUID; public class PasswordlessCreateCodeAPITest2_11 { @@ -544,4 +548,45 @@ private void checkResponse(JsonObject response, String userInputCode) { assert ((System.currentTimeMillis() - 200L) < response.get("timeCreated").getAsLong()); assertEquals(900000, response.get("codeLifetime").getAsLong()); } + + @Test + public void testPhoneNumberNormalisation() throws Exception { + String[] args = {"../"}; + + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + JsonObject createCodeRequestBody = new JsonObject(); + createCodeRequestBody.addProperty("phoneNumber", phoneNumber); + + JsonObject createCodeResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/code", createCodeRequestBody, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + checkResponse(createCodeResponse); + + HashMap params = new HashMap<>(); + + params.put("phoneNumber", phoneNumber); + + JsonObject getCodeResponse = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/codes", params, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", getCodeResponse.get("status").getAsString()); + + JsonArray devicesArray = getCodeResponse.getAsJsonArray("devices"); + assertEquals(1, devicesArray.size()); + JsonObject device = devicesArray.get(0).getAsJsonObject(); + assertEquals(normalisedPhoneNumber, device.get("phoneNumber").getAsString()); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } } diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java index fffb547b0..597c15895 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java @@ -29,6 +29,8 @@ import io.supertokens.test.httpRequest.HttpResponseException; import io.supertokens.utils.SemVer; +import io.supertokens.webserver.WebserverAPI; + import org.junit.AfterClass; import org.junit.Before; import org.junit.Rule; @@ -233,4 +235,43 @@ public void testDeleteByPhoneNumber() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + + @Test + public void testDeleteByUnnormalisedPhoneNumber() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + PasswordlessSQLStorage storage = (PasswordlessSQLStorage) StorageLayer.getStorage(process.getProcess()); + + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + String codeId = "codeId"; + + String deviceIdHash = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkFiNtJGqc"; + String linkCodeHash = "wo5UcFFVSblZEd1KOUOl-dpJ5zpSr_Qsor1Eg4TzDRE"; + + storage.createDeviceWithCode(new TenantIdentifier(null, null, null), null, normalisedPhoneNumber, "linkCodeSalt", + new PasswordlessCode(codeId, deviceIdHash, linkCodeHash, System.currentTimeMillis())); + + JsonObject removeCodesRequestBody = new JsonObject(); + removeCodesRequestBody.addProperty("phoneNumber", phoneNumber); + + JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/codes/remove", removeCodesRequestBody, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + + assertNull(storage.getDevice(new TenantIdentifier(null, null, null), deviceIdHash)); + assertNull(storage.getCode(new TenantIdentifier(null, null, null), codeId)); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } } diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java index cc5965c25..247fb9dbb 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java @@ -30,6 +30,7 @@ import io.supertokens.test.httpRequest.HttpRequestForTesting; import io.supertokens.utils.SemVer; +import io.supertokens.webserver.WebserverAPI; import io.supertokens.test.httpRequest.HttpResponseException; import org.junit.AfterClass; import org.junit.Before; @@ -326,6 +327,47 @@ public void testGetCodesWithPhoneNumber() throws Exception { assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + @Test + public void testGetCodesWithUnnormalisedPhoneNumber() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + String codeId = io.supertokens.utils.Utils.getUUID(); + + String deviceIdHash = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkFiNtJGqc="; + String linkCodeHash = "wo5UcFFVSblZEd1KOUOl-dpJ5zpSr_Qsor1Eg4TzDRE"; + + storage.createDeviceWithCode(new TenantIdentifier(null, null, null), null, normalisedPhoneNumber, + "linkCodeSalt", + new PasswordlessCode(codeId, deviceIdHash, linkCodeHash, System.currentTimeMillis())); + assertEquals(1, + storage.getDevicesByPhoneNumber(new TenantIdentifier(null, null, null), normalisedPhoneNumber).length); + + HashMap map = new HashMap<>(); + map.put("phoneNumber", phoneNumber); + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/codes", map, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + assertEquals(2, response.entrySet().size()); + assert (response.has("devices")); + JsonArray jsonDeviceList = response.get("devices").getAsJsonArray(); + assertEquals(1, jsonDeviceList.size()); + checkDevice(jsonDeviceList, 0, null, normalisedPhoneNumber, deviceIdHash, new String[]{codeId}); + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + @Test public void testGetCodesWithDeviceID() throws Exception { String[] args = { "../" }; diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java index 92d1b580d..27e236c36 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java @@ -19,7 +19,6 @@ import com.google.gson.JsonObject; import io.supertokens.ProcessState; import io.supertokens.emailpassword.EmailPassword; -import io.supertokens.passwordless.Passwordless; import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; @@ -216,6 +215,43 @@ public void testGoodInput() throws Exception { assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + @Test + public void testGetUserWithUnnormalisedPhoneNumber() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); + + // length of user ID needs to be 36 character long, otherwise it throws error + // with postgres DB + String userId = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkD"; + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + + + storage.createUser(new TenantIdentifier(null, null, null), + userId, null, normalisedPhoneNumber, System.currentTimeMillis()); + { + HashMap map = new HashMap<>(); + map.put("phoneNumber", phoneNumber); + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/user", map, 1000, 1000, null, SemVer.v2_10.get(), + "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + checkUser(response, userId, null, normalisedPhoneNumber); + } + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + private static void checkUser(JsonObject resp, String userId, String email, String phoneNumber) { assert (resp.has("user")); JsonObject user = resp.get("user").getAsJsonObject(); diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java index f620edc0f..e95582a75 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java @@ -609,4 +609,44 @@ public void testUpdatePhoneNumber() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + + @Test + public void testIfPhoneNumberIsNormalisedInUpdate() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + String userId = "6347c997-4cc9-4f95-94c9-b96e2c65aefc"; + String phoneNumber = "+442071838750"; + String updatedPhoneNumber = "+44-207 183 8751"; + String normalisedUpdatedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(updatedPhoneNumber); + + PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); + storage.createUser(new TenantIdentifier(null, null, null), + userId, null, phoneNumber, System.currentTimeMillis()); + + JsonObject updateUserRequestBody = new JsonObject(); + updateUserRequestBody.addProperty("userId", userId); + updateUserRequestBody.addProperty("phoneNumber", updatedPhoneNumber); + + JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/user", updateUserRequestBody, 1000, 1000, null, + SemVer.v2_10.get(), "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + + assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), phoneNumber).length == 0); + assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), + updatedPhoneNumber).length == 0); + assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), + normalisedUpdatedPhoneNumber).length == 1); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } } From be975c62fc42f31b0e3a85f82e7e0e517cf77657 Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Thu, 16 Nov 2023 18:57:08 +0530 Subject: [PATCH 2/3] feat: PR changes --- CHANGELOG.md | 4 + build.gradle | 4 +- .../java/io/supertokens/test/UtilsTest.java | 16 +- .../api/PasswordlessCreateCodeAPITest.java | 95 ++++++++++++ .../PasswordlessCreateCodeAPITest2_11.java | 47 +----- .../api/PasswordlessDeleteCodesAPITest.java | 92 ++++++++++++ .../PasswordlessDeleteCodesAPITest2_11.java | 43 +----- .../api/PasswordlessGetCodesAPITest.java | 137 ++++++++++++++++++ .../api/PasswordlessGetCodesAPITest2_11.java | 44 +----- .../api/PasswordlessUserGetAPITest.java | 94 ++++++++++++ .../api/PasswordlessUserGetAPITest2_11.java | 40 +---- .../api/PasswordlessUserPutAPITest.java | 92 ++++++++++++ .../api/PasswordlessUserPutAPITest2_11.java | 42 +----- 13 files changed, 534 insertions(+), 216 deletions(-) create mode 100644 src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest.java create mode 100644 src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest.java create mode 100644 src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest.java create mode 100644 src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest.java create mode 100644 src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 78d23927f..71e4ab166 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.12] - 2023-11-16 + +- Adds Phone Number normalisation + ## [7.0.11] - 2023-11-10 - Fixes email verification behaviour with user id mapping diff --git a/build.gradle b/build.gradle index 0aa76ece0..3513b61b7 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" } // } //} -version = "7.0.11" +version = "7.0.12" repositories { @@ -72,7 +72,7 @@ dependencies { implementation group: 'commons-codec', name: 'commons-codec', version: '1.15' // https://mvnrepository.com/artifact/com.googlecode.libphonenumber/libphonenumber/ - implementation group: 'com.googlecode.libphonenumber', name: 'libphonenumber', version: '8.13.12' + implementation group: 'com.googlecode.libphonenumber', name: 'libphonenumber', version: '8.13.25' compileOnly project(":supertokens-plugin-interface") testImplementation project(":supertokens-plugin-interface") diff --git a/src/test/java/io/supertokens/test/UtilsTest.java b/src/test/java/io/supertokens/test/UtilsTest.java index 583cc7573..12ef7fe7f 100644 --- a/src/test/java/io/supertokens/test/UtilsTest.java +++ b/src/test/java/io/supertokens/test/UtilsTest.java @@ -63,10 +63,18 @@ public void pubPriKeyShouldHandleBarSeparator() { @Test public void testNormalizeValidPhoneNumber() { - String inputPhoneNumber = "+1 650-555-1234"; - String expectedNormalizedPhoneNumber = "+16505551234"; - String actualNormalizedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber); - assertEquals(expectedNormalizedPhoneNumber, actualNormalizedPhoneNumber); + { + String inputPhoneNumber = "+1 650-555-1234"; + String expectedNormalizedPhoneNumber = "+16505551234"; + String actualNormalizedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber); + assertEquals(expectedNormalizedPhoneNumber, actualNormalizedPhoneNumber); + } + { + String inputPhoneNumber = "+640223334444"; + String expectedNormalizedPhoneNumber = "+64223334444"; + String actualNormalizedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(inputPhoneNumber); + assertEquals(expectedNormalizedPhoneNumber, actualNormalizedPhoneNumber); + } } @Test diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest.java new file mode 100644 index 000000000..2a511eff1 --- /dev/null +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021, 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.passwordless.api; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import io.supertokens.ProcessState; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.test.TestingProcessManager; +import io.supertokens.test.Utils; +import io.supertokens.test.httpRequest.HttpRequestForTesting; + +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 static org.junit.Assert.*; + +import java.util.HashMap; + +public class PasswordlessCreateCodeAPITest { + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void testPhoneNumberNormalisation() throws Exception { + String[] args = {"../"}; + + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + JsonObject createCodeRequestBody = new JsonObject(); + createCodeRequestBody.addProperty("phoneNumber", phoneNumber); + + JsonObject createCodeResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/code", createCodeRequestBody, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", createCodeResponse.get("status").getAsString()); + + HashMap params = new HashMap<>(); + + params.put("phoneNumber", phoneNumber); + + JsonObject getCodeResponse = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/codes", params, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", getCodeResponse.get("status").getAsString()); + + JsonArray devicesArray = getCodeResponse.getAsJsonArray("devices"); + assertEquals(1, devicesArray.size()); + JsonObject device = devicesArray.get(0).getAsJsonObject(); + assertEquals(normalisedPhoneNumber, device.get("phoneNumber").getAsString()); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java index 5561d50e7..64d1a961c 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java @@ -16,7 +16,6 @@ package io.supertokens.test.passwordless.api; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.supertokens.ProcessState; import io.supertokens.pluginInterface.STORAGE_TYPE; @@ -27,8 +26,6 @@ import io.supertokens.test.httpRequest.HttpResponseException; import io.supertokens.utils.SemVer; -import io.supertokens.webserver.WebserverAPI; - import org.junit.AfterClass; import org.junit.Before; import org.junit.Rule; @@ -38,7 +35,6 @@ import static org.junit.Assert.*; import java.util.Base64; -import java.util.HashMap; import java.util.UUID; public class PasswordlessCreateCodeAPITest2_11 { @@ -548,45 +544,4 @@ private void checkResponse(JsonObject response, String userInputCode) { assert ((System.currentTimeMillis() - 200L) < response.get("timeCreated").getAsLong()); assertEquals(900000, response.get("codeLifetime").getAsLong()); } - - @Test - public void testPhoneNumberNormalisation() throws Exception { - String[] args = {"../"}; - - String phoneNumber = "+44-207 183 8750"; - String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); - - TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); - - if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { - return; - } - - JsonObject createCodeRequestBody = new JsonObject(); - createCodeRequestBody.addProperty("phoneNumber", phoneNumber); - - JsonObject createCodeResponse = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", - "http://localhost:3567/recipe/signinup/code", createCodeRequestBody, 1000, 1000, null, - WebserverAPI.getLatestCDIVersion().get(), "passwordless"); - checkResponse(createCodeResponse); - - HashMap params = new HashMap<>(); - - params.put("phoneNumber", phoneNumber); - - JsonObject getCodeResponse = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", - "http://localhost:3567/recipe/signinup/codes", params, 1000, 1000, null, - WebserverAPI.getLatestCDIVersion().get(), "passwordless"); - - assertEquals("OK", getCodeResponse.get("status").getAsString()); - - JsonArray devicesArray = getCodeResponse.getAsJsonArray("devices"); - assertEquals(1, devicesArray.size()); - JsonObject device = devicesArray.get(0).getAsJsonObject(); - assertEquals(normalisedPhoneNumber, device.get("phoneNumber").getAsString()); - - process.kill(); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); - } -} +} \ No newline at end of file diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest.java new file mode 100644 index 000000000..28d687e85 --- /dev/null +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021, 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.passwordless.api; + +import com.google.gson.JsonObject; +import io.supertokens.ProcessState; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.passwordless.PasswordlessCode; +import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.test.TestingProcessManager; +import io.supertokens.test.Utils; +import io.supertokens.test.httpRequest.HttpRequestForTesting; + +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 static org.junit.Assert.*; + +public class PasswordlessDeleteCodesAPITest { + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void testDeleteByUnnormalisedPhoneNumber() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + PasswordlessSQLStorage storage = (PasswordlessSQLStorage) StorageLayer.getStorage(process.getProcess()); + + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + String codeId = "codeId"; + + String deviceIdHash = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkFiNtJGqc"; + String linkCodeHash = "wo5UcFFVSblZEd1KOUOl-dpJ5zpSr_Qsor1Eg4TzDRE"; + + storage.createDeviceWithCode(new TenantIdentifier(null, null, null), null, normalisedPhoneNumber, "linkCodeSalt", + new PasswordlessCode(codeId, deviceIdHash, linkCodeHash, System.currentTimeMillis())); + + JsonObject removeCodesRequestBody = new JsonObject(); + removeCodesRequestBody.addProperty("phoneNumber", phoneNumber); + + JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/codes/remove", removeCodesRequestBody, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + + assertNull(storage.getDevice(new TenantIdentifier(null, null, null), deviceIdHash)); + assertNull(storage.getCode(new TenantIdentifier(null, null, null), codeId)); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java index 597c15895..3dbcbb4ec 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java @@ -29,8 +29,6 @@ import io.supertokens.test.httpRequest.HttpResponseException; import io.supertokens.utils.SemVer; -import io.supertokens.webserver.WebserverAPI; - import org.junit.AfterClass; import org.junit.Before; import org.junit.Rule; @@ -235,43 +233,4 @@ public void testDeleteByPhoneNumber() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } - - @Test - public void testDeleteByUnnormalisedPhoneNumber() throws Exception { - String[] args = {"../"}; - - TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); - - if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { - return; - } - - PasswordlessSQLStorage storage = (PasswordlessSQLStorage) StorageLayer.getStorage(process.getProcess()); - - String phoneNumber = "+44-207 183 8750"; - String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); - String codeId = "codeId"; - - String deviceIdHash = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkFiNtJGqc"; - String linkCodeHash = "wo5UcFFVSblZEd1KOUOl-dpJ5zpSr_Qsor1Eg4TzDRE"; - - storage.createDeviceWithCode(new TenantIdentifier(null, null, null), null, normalisedPhoneNumber, "linkCodeSalt", - new PasswordlessCode(codeId, deviceIdHash, linkCodeHash, System.currentTimeMillis())); - - JsonObject removeCodesRequestBody = new JsonObject(); - removeCodesRequestBody.addProperty("phoneNumber", phoneNumber); - - JsonObject response = HttpRequestForTesting.sendJsonPOSTRequest(process.getProcess(), "", - "http://localhost:3567/recipe/signinup/codes/remove", removeCodesRequestBody, 1000, 1000, null, - WebserverAPI.getLatestCDIVersion().get(), "passwordless"); - - assertEquals("OK", response.get("status").getAsString()); - - assertNull(storage.getDevice(new TenantIdentifier(null, null, null), deviceIdHash)); - assertNull(storage.getCode(new TenantIdentifier(null, null, null), codeId)); - - process.kill(); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); - } -} +} \ No newline at end of file diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest.java new file mode 100644 index 000000000..774772bba --- /dev/null +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021, 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.passwordless.api; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import io.supertokens.ProcessState; +import io.supertokens.passwordless.Passwordless; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.passwordless.PasswordlessCode; +import io.supertokens.pluginInterface.passwordless.PasswordlessStorage; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.test.TestingProcessManager; +import io.supertokens.test.Utils; +import io.supertokens.test.httpRequest.HttpRequestForTesting; + +import io.supertokens.utils.SemVer; +import io.supertokens.webserver.WebserverAPI; +import io.supertokens.test.httpRequest.HttpResponseException; +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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class PasswordlessGetCodesAPITest { + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void testGetCodesWithUnnormalisedPhoneNumber() throws Exception { + String[] args = { "../" }; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + String codeId = io.supertokens.utils.Utils.getUUID(); + + String deviceIdHash = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkFiNtJGqc="; + String linkCodeHash = "wo5UcFFVSblZEd1KOUOl-dpJ5zpSr_Qsor1Eg4TzDRE"; + + storage.createDeviceWithCode(new TenantIdentifier(null, null, null), null, normalisedPhoneNumber, + "linkCodeSalt", + new PasswordlessCode(codeId, deviceIdHash, linkCodeHash, System.currentTimeMillis())); + assertEquals(1, + storage.getDevicesByPhoneNumber(new TenantIdentifier(null, null, null), normalisedPhoneNumber).length); + + HashMap map = new HashMap<>(); + map.put("phoneNumber", phoneNumber); + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/signinup/codes", map, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + assertEquals(2, response.entrySet().size()); + assert (response.has("devices")); + JsonArray jsonDeviceList = response.get("devices").getAsJsonArray(); + assertEquals(1, jsonDeviceList.size()); + checkDevice(jsonDeviceList, 0, null, normalisedPhoneNumber, deviceIdHash, new String[] { codeId }); + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + + private void checkDevice(JsonArray jsonDeviceList, int ind, String email, String phoneNumber, String deviceIdHash, + String[] codeIds) { + JsonObject device = jsonDeviceList.get(ind).getAsJsonObject(); + + assertEquals(deviceIdHash, device.get("preAuthSessionId").getAsString()); + assertEquals(0, device.get("failedCodeInputAttemptCount").getAsInt()); + + if (email == null) { + assert (!device.has("email")); + } else { + assertEquals(email, device.get("email").getAsString()); + } + + if (phoneNumber == null) { + assert (!device.has("phoneNumber")); + } else { + assertEquals(phoneNumber, device.get("phoneNumber").getAsString()); + } + + assert (device.has("codes")); + JsonArray jsonCodeList = device.get("codes").getAsJsonArray(); + assertEquals(codeIds.length, jsonCodeList.size()); + for (int i = 0; i < codeIds.length; ++i) { + checkCodeInJsonArray(jsonCodeList, i, codeIds[i]); + } + + assertEquals(4, device.entrySet().size()); + } + + private void checkCodeInJsonArray(JsonArray jsonCodeList, int index, String codeId2) { + JsonObject code = jsonCodeList.get(index).getAsJsonObject(); + assertEquals(codeId2, code.get("codeId").getAsString()); + assert (code.has("timeCreated")); + assert (code.has("codeLifetime")); + assertEquals(3, code.entrySet().size()); + } +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java index 247fb9dbb..009289a3b 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java @@ -30,7 +30,6 @@ import io.supertokens.test.httpRequest.HttpRequestForTesting; import io.supertokens.utils.SemVer; -import io.supertokens.webserver.WebserverAPI; import io.supertokens.test.httpRequest.HttpResponseException; import org.junit.AfterClass; import org.junit.Before; @@ -327,47 +326,6 @@ public void testGetCodesWithPhoneNumber() throws Exception { assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } - @Test - public void testGetCodesWithUnnormalisedPhoneNumber() throws Exception { - String[] args = {"../"}; - - TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); - - if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { - return; - } - - PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); - String phoneNumber = "+44-207 183 8750"; - String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); - String codeId = io.supertokens.utils.Utils.getUUID(); - - String deviceIdHash = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkFiNtJGqc="; - String linkCodeHash = "wo5UcFFVSblZEd1KOUOl-dpJ5zpSr_Qsor1Eg4TzDRE"; - - storage.createDeviceWithCode(new TenantIdentifier(null, null, null), null, normalisedPhoneNumber, - "linkCodeSalt", - new PasswordlessCode(codeId, deviceIdHash, linkCodeHash, System.currentTimeMillis())); - assertEquals(1, - storage.getDevicesByPhoneNumber(new TenantIdentifier(null, null, null), normalisedPhoneNumber).length); - - HashMap map = new HashMap<>(); - map.put("phoneNumber", phoneNumber); - JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", - "http://localhost:3567/recipe/signinup/codes", map, 1000, 1000, null, - WebserverAPI.getLatestCDIVersion().get(), "passwordless"); - - assertEquals("OK", response.get("status").getAsString()); - assertEquals(2, response.entrySet().size()); - assert (response.has("devices")); - JsonArray jsonDeviceList = response.get("devices").getAsJsonArray(); - assertEquals(1, jsonDeviceList.size()); - checkDevice(jsonDeviceList, 0, null, normalisedPhoneNumber, deviceIdHash, new String[]{codeId}); - process.kill(); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); - } - @Test public void testGetCodesWithDeviceID() throws Exception { String[] args = { "../" }; @@ -464,4 +422,4 @@ private void checkCodeInJsonArray(JsonArray jsonCodeList, int index, String code assert (code.has("codeLifetime")); assertEquals(3, code.entrySet().size()); } -} +} \ No newline at end of file diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest.java new file mode 100644 index 000000000..58265510d --- /dev/null +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021, 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.passwordless.api; + +import com.google.gson.JsonObject; +import io.supertokens.ProcessState; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.passwordless.PasswordlessStorage; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.test.TestingProcessManager; +import io.supertokens.test.Utils; +import io.supertokens.test.httpRequest.HttpRequestForTesting; + +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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class PasswordlessUserGetAPITest { + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void testGetUserWithUnnormalisedPhoneNumber() throws Exception { + String[] args = { "../" }; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); + + // length of user ID needs to be 36 character long, otherwise it throws error + // with postgres DB + String userId = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkD"; + String phoneNumber = "+44-207 183 8750"; + String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); + + storage.createUser(new TenantIdentifier(null, null, null), + userId, null, normalisedPhoneNumber, System.currentTimeMillis()); + { + HashMap map = new HashMap<>(); + map.put("phoneNumber", phoneNumber); + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/recipe/user", map, 1000, 1000, null, WebserverAPI.getLatestCDIVersion().get(), + "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + assert (response.has("user")); + JsonObject user = response.get("user").getAsJsonObject(); + assertEquals(user.get("phoneNumbers").getAsJsonArray().get(0).getAsString(), normalisedPhoneNumber); + } + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java index 27e236c36..6ee0fac8b 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java @@ -19,6 +19,7 @@ import com.google.gson.JsonObject; import io.supertokens.ProcessState; import io.supertokens.emailpassword.EmailPassword; +import io.supertokens.passwordless.Passwordless; import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; @@ -215,43 +216,6 @@ public void testGoodInput() throws Exception { assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } - @Test - public void testGetUserWithUnnormalisedPhoneNumber() throws Exception { - String[] args = {"../"}; - - TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); - - if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { - return; - } - - PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); - - // length of user ID needs to be 36 character long, otherwise it throws error - // with postgres DB - String userId = "pZ9SP0USbXbejGFO6qx7x3JBjupJZVtw4RkD"; - String phoneNumber = "+44-207 183 8750"; - String normalisedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(phoneNumber); - - - storage.createUser(new TenantIdentifier(null, null, null), - userId, null, normalisedPhoneNumber, System.currentTimeMillis()); - { - HashMap map = new HashMap<>(); - map.put("phoneNumber", phoneNumber); - JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", - "http://localhost:3567/recipe/user", map, 1000, 1000, null, SemVer.v2_10.get(), - "passwordless"); - - assertEquals("OK", response.get("status").getAsString()); - checkUser(response, userId, null, normalisedPhoneNumber); - } - - process.kill(); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); - } - private static void checkUser(JsonObject resp, String userId, String email, String phoneNumber) { assert (resp.has("user")); JsonObject user = resp.get("user").getAsJsonObject(); @@ -311,4 +275,4 @@ public void testGetUserForUsersOfOtherRecipeIds() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } -} +} \ No newline at end of file diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest.java new file mode 100644 index 000000000..0ea5cf86b --- /dev/null +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021, 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.passwordless.api; + +import com.google.gson.JsonObject; +import io.supertokens.ProcessState; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.passwordless.PasswordlessStorage; +import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.test.TestingProcessManager; +import io.supertokens.test.Utils; +import io.supertokens.test.httpRequest.HttpRequestForTesting; +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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class PasswordlessUserPutAPITest { + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void testIfPhoneNumberIsNormalisedInUpdate() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + String userId = "6347c997-4cc9-4f95-94c9-b96e2c65aefc"; + String phoneNumber = "+442071838750"; + String updatedPhoneNumber = "+44-207 183 8751"; + String normalisedUpdatedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(updatedPhoneNumber); + + PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); + storage.createUser(new TenantIdentifier(null, null, null), + userId, null, phoneNumber, System.currentTimeMillis()); + + JsonObject updateUserRequestBody = new JsonObject(); + updateUserRequestBody.addProperty("recipeUserId", userId); + updateUserRequestBody.addProperty("phoneNumber", updatedPhoneNumber); + + JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", + "http://localhost:3567/recipe/user", updateUserRequestBody, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), "passwordless"); + + assertEquals("OK", response.get("status").getAsString()); + + assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), phoneNumber).length == 0); + assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), + updatedPhoneNumber).length == 0); + assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), + normalisedUpdatedPhoneNumber).length == 1); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java index e95582a75..cec5b931b 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java @@ -609,44 +609,4 @@ public void testUpdatePhoneNumber() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } - - @Test - public void testIfPhoneNumberIsNormalisedInUpdate() throws Exception { - String[] args = {"../"}; - - TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); - - if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { - return; - } - - String userId = "6347c997-4cc9-4f95-94c9-b96e2c65aefc"; - String phoneNumber = "+442071838750"; - String updatedPhoneNumber = "+44-207 183 8751"; - String normalisedUpdatedPhoneNumber = io.supertokens.utils.Utils.normalizeIfPhoneNumber(updatedPhoneNumber); - - PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess()); - storage.createUser(new TenantIdentifier(null, null, null), - userId, null, phoneNumber, System.currentTimeMillis()); - - JsonObject updateUserRequestBody = new JsonObject(); - updateUserRequestBody.addProperty("userId", userId); - updateUserRequestBody.addProperty("phoneNumber", updatedPhoneNumber); - - JsonObject response = HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", - "http://localhost:3567/recipe/user", updateUserRequestBody, 1000, 1000, null, - SemVer.v2_10.get(), "passwordless"); - - assertEquals("OK", response.get("status").getAsString()); - - assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), phoneNumber).length == 0); - assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), - updatedPhoneNumber).length == 0); - assert (storage.listPrimaryUsersByPhoneNumber(new TenantIdentifier(null, null, null), - normalisedUpdatedPhoneNumber).length == 1); - - process.kill(); - assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); - } -} +} \ No newline at end of file From 8bbcd8554477bf4ef3f4bb64b0d0c2eb5a7df567 Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Thu, 16 Nov 2023 19:07:28 +0530 Subject: [PATCH 3/3] feat: PR changes --- .../passwordless/api/PasswordlessCreateCodeAPITest2_11.java | 2 +- .../passwordless/api/PasswordlessDeleteCodesAPITest2_11.java | 2 +- .../test/passwordless/api/PasswordlessGetCodesAPITest2_11.java | 2 +- .../test/passwordless/api/PasswordlessUserGetAPITest2_11.java | 2 +- .../test/passwordless/api/PasswordlessUserPutAPITest2_11.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java index 64d1a961c..596382a61 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessCreateCodeAPITest2_11.java @@ -544,4 +544,4 @@ private void checkResponse(JsonObject response, String userInputCode) { assert ((System.currentTimeMillis() - 200L) < response.get("timeCreated").getAsLong()); assertEquals(900000, response.get("codeLifetime").getAsLong()); } -} \ No newline at end of file +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java index 3dbcbb4ec..fffb547b0 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessDeleteCodesAPITest2_11.java @@ -233,4 +233,4 @@ public void testDeleteByPhoneNumber() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } -} \ No newline at end of file +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java index 009289a3b..cc5965c25 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessGetCodesAPITest2_11.java @@ -422,4 +422,4 @@ private void checkCodeInJsonArray(JsonArray jsonCodeList, int index, String code assert (code.has("codeLifetime")); assertEquals(3, code.entrySet().size()); } -} \ No newline at end of file +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java index 6ee0fac8b..92d1b580d 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserGetAPITest2_11.java @@ -275,4 +275,4 @@ public void testGetUserForUsersOfOtherRecipeIds() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } -} \ No newline at end of file +} diff --git a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java index cec5b931b..f620edc0f 100644 --- a/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java +++ b/src/test/java/io/supertokens/test/passwordless/api/PasswordlessUserPutAPITest2_11.java @@ -609,4 +609,4 @@ public void testUpdatePhoneNumber() throws Exception { process.kill(); assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } -} \ No newline at end of file +}