Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/java/io/supertokens/authRecipe/AuthRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.supertokens.pluginInterface.dashboard.DashboardSearchTags;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
Expand Down Expand Up @@ -192,6 +193,16 @@ public static void deleteUser(Main main, String userId)
deleteUser(appIdentifier, userId, mapping);
}

@TestOnly
public static void deleteUser(AppIdentifierWithStorage appIdentifierWithStorage, String userId)
throws StorageQueryException, StorageTransactionLogicException {
Storage storage = appIdentifierWithStorage.getStorage();
UserIdMapping mapping = io.supertokens.useridmapping.UserIdMapping.getUserIdMapping(appIdentifierWithStorage,
userId, UserIdType.ANY);

deleteUser(appIdentifierWithStorage, userId, mapping);
}

private static void deleteNonAuthRecipeUser(AppIdentifierWithStorage
appIdentifierWithStorage, String userId)
throws StorageQueryException, StorageTransactionLogicException {
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/io/supertokens/multitenancy/Multitenancy.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ private static void validateConfigJsonForInvalidKeys(Main main, JsonObject coreC
}
}

private static void validateTenantConfig(Main main, TenantConfig targetTenantConfig, boolean shouldPreventDbConfigUpdate)
private static void validateTenantConfig(Main main, TenantConfig targetTenantConfig, boolean shouldPreventDbConfigUpdate,
boolean skipThirdPartyConfigValidation)
throws IOException, InvalidConfigException, InvalidProviderConfigException, BadPermissionException,
TenantOrAppNotFoundException, CannotModifyBaseConfigException {

Expand Down Expand Up @@ -172,7 +173,7 @@ private static void validateTenantConfig(Main main, TenantConfig targetTenantCon
}

// validate third party config
{
if (!skipThirdPartyConfigValidation) {
ThirdParty.verifyThirdPartyProvidersArray(targetTenantConfig.thirdPartyConfig.providers);
}
}
Expand All @@ -190,6 +191,13 @@ public static boolean addNewOrUpdateAppOrTenant(Main main, TenantConfig newTenan
throws CannotModifyBaseConfigException, BadPermissionException,
StorageQueryException, FeatureNotEnabledException, IOException, InvalidConfigException,
InvalidProviderConfigException, TenantOrAppNotFoundException {
return addNewOrUpdateAppOrTenant(main, newTenant, shouldPreventDbConfigUpdate, false);
}

public static boolean addNewOrUpdateAppOrTenant(Main main, TenantConfig newTenant, boolean shouldPreventDbConfigUpdate, boolean skipThirdPartyConfigValidation)
throws CannotModifyBaseConfigException, BadPermissionException,
StorageQueryException, FeatureNotEnabledException, IOException, InvalidConfigException,
InvalidProviderConfigException, TenantOrAppNotFoundException {

if (StorageLayer.getBaseStorage(main).getType() != STORAGE_TYPE.SQL) {
if (newTenant.tenantIdentifier.equals(TenantIdentifier.BASE_TENANT)) {
Expand All @@ -204,7 +212,7 @@ public static boolean addNewOrUpdateAppOrTenant(Main main, TenantConfig newTenan
// a big issue at the moment, but we want to solve this by taking appropriate database locks on
// connectionuridomain, appid and tenantid.

validateTenantConfig(main, newTenant, shouldPreventDbConfigUpdate);
validateTenantConfig(main, newTenant, shouldPreventDbConfigUpdate, skipThirdPartyConfigValidation);

boolean creationInSharedDbSucceeded = false;
List<TenantIdentifier> tenantsThatChanged = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
String thirdPartyId = InputParser.parseStringOrThrowError(input, "thirdPartyId", false);
thirdPartyId = thirdPartyId.trim();

Boolean skipValidation = InputParser.parseBooleanOrThrowError(input, "skipValidation", true);
if (skipValidation == null) {
skipValidation = false;
}

try {
TenantIdentifierWithStorage tenantIdentifier = this.getTenantIdentifierWithStorageFromRequest(req);

Expand Down Expand Up @@ -106,7 +111,7 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
config.passwordlessConfig,
config.coreConfig);

Multitenancy.addNewOrUpdateAppOrTenant(main, updatedConfig, shouldProtectDbConfig(req));
Multitenancy.addNewOrUpdateAppOrTenant(main, updatedConfig, shouldProtectDbConfig(req), skipValidation);

JsonObject result = new JsonObject();
result.addProperty("status", "OK");
Expand Down
89 changes: 88 additions & 1 deletion src/test/java/io/supertokens/test/CronjobTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@

package io.supertokens.test;

import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.cronjobs.CronTask;
import io.supertokens.cronjobs.Cronjobs;
import io.supertokens.exceptions.QuitProgramException;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.*;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.storageLayer.StorageLayer;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
Expand Down Expand Up @@ -250,4 +255,86 @@ public void testAddingCronJobTwice() throws Exception {
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

@Test
public void testAddingTenantsDoesNotIncreaseCronJobs() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY});
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

int initialSize = Cronjobs.getInstance(process.getProcess()).getTasks().size();

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a1", null),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
), false);
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a1", "t1"),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
), false);
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a2", null),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
), false);
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a2", "t2"),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
), false);

assertEquals(initialSize, Cronjobs.getInstance(process.getProcess()).getTasks().size());

JsonObject config = new JsonObject();
StorageLayer.getStorage(process.getProcess()).modifyConfigToAddANewUserPoolForTesting(config, 1);

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a3", null),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
config
), false);

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a3", "t1"),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
config
), false);
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a4", null),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
config
), false);
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "a4", "t2"),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
config
), false);

assertEquals(initialSize, Cronjobs.getInstance(process.getProcess()).getTasks().size());

process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}
}
168 changes: 168 additions & 0 deletions src/test/java/io/supertokens/test/HelloAPITest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* 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;

import com.google.gson.JsonObject;
import io.supertokens.ProcessState;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.pluginInterface.STORAGE_TYPE;
import io.supertokens.pluginInterface.multitenancy.*;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.test.httpRequest.HttpRequestForTesting;
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 HelloAPITest {
@Rule
public TestRule watchman = Utils.getOnFailure();

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

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

@Test
public void testHelloAPIWithBasePath1() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY});
Utils.setValueInConfig("base_path", "/base");
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

String res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/base", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/base/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

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

@Test
public void testHelloAPIWithBasePath2() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY});
Utils.setValueInConfig("base_path", "/hello");
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

String res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/hello/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

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

@Test
public void testHelloAPIWithBasePath3() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY});
Utils.setValueInConfig("base_path", "/hello");
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

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

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "hello", null),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
), false);

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, "hello", "hello"),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
), false);

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier(null, null, "hello"),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
), false);

String res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/hello/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/hello/hello/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/hello/appid-hello/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/hello/appid-hello/hello/hello", null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
assertEquals("Hello", res);

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