diff --git a/src/main/java/io/supertokens/webserver/api/accountlinking/CanCreatePrimaryUserAPI.java b/src/main/java/io/supertokens/webserver/api/accountlinking/CanCreatePrimaryUserAPI.java index 327f9cc50..9457ddd0d 100644 --- a/src/main/java/io/supertokens/webserver/api/accountlinking/CanCreatePrimaryUserAPI.java +++ b/src/main/java/io/supertokens/webserver/api/accountlinking/CanCreatePrimaryUserAPI.java @@ -22,6 +22,7 @@ import io.supertokens.authRecipe.AuthRecipe; import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException; import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException; +import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage; @@ -39,7 +40,7 @@ public class CanCreatePrimaryUserAPI extends WebserverAPI { public CanCreatePrimaryUserAPI(Main main) { - super(main, ""); + super(main, RECIPE_ID.ACCOUNT_LINKING.toString()); } @Override diff --git a/src/main/java/io/supertokens/webserver/api/accountlinking/CanLinkAccountsAPI.java b/src/main/java/io/supertokens/webserver/api/accountlinking/CanLinkAccountsAPI.java index f408cfca2..93d1714ba 100644 --- a/src/main/java/io/supertokens/webserver/api/accountlinking/CanLinkAccountsAPI.java +++ b/src/main/java/io/supertokens/webserver/api/accountlinking/CanLinkAccountsAPI.java @@ -23,6 +23,7 @@ import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException; import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException; import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException; +import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage; @@ -40,7 +41,7 @@ public class CanLinkAccountsAPI extends WebserverAPI { public CanLinkAccountsAPI(Main main) { - super(main, ""); + super(main, RECIPE_ID.ACCOUNT_LINKING.toString()); } @Override diff --git a/src/main/java/io/supertokens/webserver/api/accountlinking/CreatePrimaryUserAPI.java b/src/main/java/io/supertokens/webserver/api/accountlinking/CreatePrimaryUserAPI.java index 0eb764238..8573650b7 100644 --- a/src/main/java/io/supertokens/webserver/api/accountlinking/CreatePrimaryUserAPI.java +++ b/src/main/java/io/supertokens/webserver/api/accountlinking/CreatePrimaryUserAPI.java @@ -23,6 +23,7 @@ import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException; import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException; import io.supertokens.featureflag.exceptions.FeatureNotEnabledException; +import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage; @@ -40,7 +41,7 @@ public class CreatePrimaryUserAPI extends WebserverAPI { public CreatePrimaryUserAPI(Main main) { - super(main, ""); + super(main, RECIPE_ID.ACCOUNT_LINKING.toString()); } @Override diff --git a/src/main/java/io/supertokens/webserver/api/accountlinking/LinkAccountsAPI.java b/src/main/java/io/supertokens/webserver/api/accountlinking/LinkAccountsAPI.java index 06cad0a9f..279b5d189 100644 --- a/src/main/java/io/supertokens/webserver/api/accountlinking/LinkAccountsAPI.java +++ b/src/main/java/io/supertokens/webserver/api/accountlinking/LinkAccountsAPI.java @@ -24,6 +24,7 @@ import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException; import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException; import io.supertokens.featureflag.exceptions.FeatureNotEnabledException; +import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage; @@ -41,7 +42,7 @@ public class LinkAccountsAPI extends WebserverAPI { public LinkAccountsAPI(Main main) { - super(main, ""); + super(main, RECIPE_ID.ACCOUNT_LINKING.toString()); } @Override diff --git a/src/main/java/io/supertokens/webserver/api/accountlinking/UnlinkAccountAPI.java b/src/main/java/io/supertokens/webserver/api/accountlinking/UnlinkAccountAPI.java index 5176b5e48..3abed0328 100644 --- a/src/main/java/io/supertokens/webserver/api/accountlinking/UnlinkAccountAPI.java +++ b/src/main/java/io/supertokens/webserver/api/accountlinking/UnlinkAccountAPI.java @@ -21,6 +21,7 @@ import io.supertokens.Main; import io.supertokens.authRecipe.AuthRecipe; import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException; +import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage; @@ -37,7 +38,7 @@ public class UnlinkAccountAPI extends WebserverAPI { public UnlinkAccountAPI(Main main) { - super(main, ""); + super(main, RECIPE_ID.ACCOUNT_LINKING.toString()); } @Override diff --git a/src/test/java/io/supertokens/test/authRecipe/GetUserByIdAPITest.java b/src/test/java/io/supertokens/test/authRecipe/GetUserByIdAPITest.java new file mode 100644 index 000000000..133ab4b6d --- /dev/null +++ b/src/test/java/io/supertokens/test/authRecipe/GetUserByIdAPITest.java @@ -0,0 +1,231 @@ +/* + * 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.authRecipe; + +import com.google.gson.JsonObject; +import io.supertokens.ProcessState; +import io.supertokens.authRecipe.AuthRecipe; +import io.supertokens.emailpassword.EmailPassword; +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 GetUserByIdAPITest { + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void getUserSuccess() 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 user = EmailPassword.signUp(process.getProcess(), "test@example.com", "password"); + assert (!user.isPrimaryUser); + + Thread.sleep(50); + + AuthRecipeUserInfo user2 = EmailPassword.signUp(process.getProcess(), "test2@example.com", "password"); + assert (!user2.isPrimaryUser); + + AuthRecipe.createPrimaryUser(process.main, user.id); + + AuthRecipe.linkAccounts(process.main, user2.id, user.id); + + { + Map params = new HashMap<>(); + params.put("userId", user2.id); + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/user/id", params, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), ""); + assertEquals(2, response.entrySet().size()); + assertEquals("OK", response.get("status").getAsString()); + JsonObject jsonUser = response.get("user").getAsJsonObject(); + assert (jsonUser.get("id").getAsString().equals(user.id)); + assert (jsonUser.get("timeJoined").getAsLong() == user.timeJoined); + assert (jsonUser.get("isPrimaryUser").getAsBoolean()); + assert (jsonUser.get("emails").getAsJsonArray().size() == 2); + assert (jsonUser.get("emails").getAsJsonArray().get(0).getAsString().equals("test@example.com") || + jsonUser.get("emails").getAsJsonArray().get(0).getAsString().equals("test2@example.com")); + assert (jsonUser.get("emails").getAsJsonArray().get(1).getAsString().equals("test@example.com") || + jsonUser.get("emails").getAsJsonArray().get(1).getAsString().equals("test2@example.com")); + assert (!jsonUser.get("emails").getAsJsonArray().get(1).getAsString() + .equals(jsonUser.get("emails").getAsJsonArray().get(0).getAsString())); + assert (jsonUser.get("phoneNumbers").getAsJsonArray().size() == 0); + assert (jsonUser.get("thirdParty").getAsJsonArray().size() == 0); + assert (jsonUser.get("loginMethods").getAsJsonArray().size() == 2); + { + JsonObject lM = jsonUser.get("loginMethods").getAsJsonArray().get(0).getAsJsonObject(); + assertFalse(lM.get("verified").getAsBoolean()); + assertEquals(lM.get("timeJoined").getAsLong(), user.timeJoined); + assertEquals(lM.get("recipeUserId").getAsString(), user.id); + assertEquals(lM.get("recipeId").getAsString(), "emailpassword"); + assertEquals(lM.get("email").getAsString(), "test@example.com"); + assert (lM.entrySet().size() == 6); + } + { + JsonObject lM = jsonUser.get("loginMethods").getAsJsonArray().get(1).getAsJsonObject(); + assertFalse(lM.get("verified").getAsBoolean()); + assertEquals(lM.get("timeJoined").getAsLong(), user2.timeJoined); + assertEquals(lM.get("recipeUserId").getAsString(), user2.id); + assertEquals(lM.get("recipeId").getAsString(), "emailpassword"); + assertEquals(lM.get("email").getAsString(), "test2@example.com"); + assert (lM.entrySet().size() == 6); + } + } + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + + @Test + public void getUserSuccessWithUserIdMapping() 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 user = EmailPassword.signUp(process.getProcess(), "test@example.com", "password"); + assert (!user.isPrimaryUser); + UserIdMapping.createUserIdMapping(process.main, user.id, "e1", null, false); + + Thread.sleep(50); + + AuthRecipeUserInfo user2 = EmailPassword.signUp(process.getProcess(), "test2@example.com", "password"); + assert (!user2.isPrimaryUser); + UserIdMapping.createUserIdMapping(process.main, user2.id, "e2", null, false); + + AuthRecipe.createPrimaryUser(process.main, user.id); + + AuthRecipe.linkAccounts(process.main, user2.id, user.id); + + { + Map params = new HashMap<>(); + params.put("userId", "e2"); + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/user/id", params, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), ""); + assertEquals(2, response.entrySet().size()); + assertEquals("OK", response.get("status").getAsString()); + JsonObject jsonUser = response.get("user").getAsJsonObject(); + assert (jsonUser.get("id").getAsString().equals("e1")); + assert (jsonUser.get("timeJoined").getAsLong() == user.timeJoined); + assert (jsonUser.get("isPrimaryUser").getAsBoolean()); + assert (jsonUser.get("emails").getAsJsonArray().size() == 2); + assert (jsonUser.get("emails").getAsJsonArray().get(0).getAsString().equals("test@example.com") || + jsonUser.get("emails").getAsJsonArray().get(0).getAsString().equals("test2@example.com")); + assert (jsonUser.get("emails").getAsJsonArray().get(1).getAsString().equals("test@example.com") || + jsonUser.get("emails").getAsJsonArray().get(1).getAsString().equals("test2@example.com")); + assert (!jsonUser.get("emails").getAsJsonArray().get(1).getAsString() + .equals(jsonUser.get("emails").getAsJsonArray().get(0).getAsString())); + assert (jsonUser.get("phoneNumbers").getAsJsonArray().size() == 0); + assert (jsonUser.get("thirdParty").getAsJsonArray().size() == 0); + assert (jsonUser.get("loginMethods").getAsJsonArray().size() == 2); + { + JsonObject lM = jsonUser.get("loginMethods").getAsJsonArray().get(0).getAsJsonObject(); + assertFalse(lM.get("verified").getAsBoolean()); + assertEquals(lM.get("timeJoined").getAsLong(), user.timeJoined); + assertEquals(lM.get("recipeUserId").getAsString(), "e1"); + assertEquals(lM.get("recipeId").getAsString(), "emailpassword"); + assertEquals(lM.get("email").getAsString(), "test@example.com"); + assert (lM.entrySet().size() == 6); + } + { + JsonObject lM = jsonUser.get("loginMethods").getAsJsonArray().get(1).getAsJsonObject(); + assertFalse(lM.get("verified").getAsBoolean()); + assertEquals(lM.get("timeJoined").getAsLong(), user2.timeJoined); + assertEquals(lM.get("recipeUserId").getAsString(), user2.id); + assertEquals(lM.get("recipeId").getAsString(), "emailpassword"); + assertEquals(lM.get("email").getAsString(), "test2@example.com"); + assert (lM.entrySet().size() == 6); + } + } + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + + @Test + public void getUnknownUser() 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; + } + + { + Map params = new HashMap<>(); + params.put("userId", "random"); + JsonObject response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/user/id", params, 1000, 1000, null, + WebserverAPI.getLatestCDIVersion().get(), ""); + assertEquals(1, response.entrySet().size()); + assertEquals("UNKNOWN_USER_ID_ERROR", response.get("status").getAsString()); + } + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } +}