Skip to content

Commit 48754e2

Browse files
authored
fix: load only cud config in core (#920)
* fix: load only cud * fix: connection pool handling * fix: tests * fix: version update * fix: tests
1 parent 01cef02 commit 48754e2

File tree

13 files changed

+589
-35
lines changed

13 files changed

+589
-35
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [unreleased]
99

10+
## [6.0.17] - 2024-02-06
11+
12+
- Adds new config `supertokens_saas_load_only_cud` that makes the core instance load a particular CUD only, irrespective of the CUDs present in the db.
13+
- Fixes connection pool handling when connection pool size changes for a tenant.
14+
1015
## [6.0.16] - 2023-11-03
1116

1217
- Collects requests stats per app

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
1919
// }
2020
//}
2121

22-
version = "6.0.16"
22+
version = "6.0.17"
2323

2424

2525
repositories {

config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,7 @@ core_config_version: 0
146146
# when CDI version is not specified in the request. When set to null, the core will assume the latest version of the
147147
# CDI.
148148
# supertokens_max_cdi_version:
149+
150+
# (OPTIONAL | Default: null) string value. If specified, the supertokens service will only load the specified CUD even
151+
# if there are more CUDs in the database and block all other CUDs from being used from this instance.
152+
# supertokens_saas_load_only_cud:

devConfig.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,7 @@ disable_telemetry: true
147147
# when CDI version is not specified in the request. When set to null, the core will assume the latest version of the
148148
# CDI.
149149
# supertokens_max_cdi_version:
150+
151+
# (OPTIONAL | Default: null) string value. If specified, the supertokens service will only load the specified CUD even
152+
# if there are more CUDs in the database and block all other CUDs from being used from this instance.
153+
# supertokens_saas_load_only_cud:

src/main/java/io/supertokens/config/CoreConfig.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
import io.supertokens.pluginInterface.LOG_LEVEL;
3131
import io.supertokens.pluginInterface.exceptions.InvalidConfigException;
3232
import io.supertokens.utils.SemVer;
33+
import io.supertokens.webserver.Utils;
3334
import io.supertokens.webserver.WebserverAPI;
35+
import jakarta.servlet.ServletException;
3436
import org.apache.catalina.filters.RemoteAddrFilter;
3537
import org.jetbrains.annotations.TestOnly;
3638

@@ -197,6 +199,10 @@ public class CoreConfig {
197199
@JsonProperty
198200
private String supertokens_max_cdi_version = null;
199201

202+
@ConfigYamlOnly
203+
@JsonProperty
204+
private String supertokens_saas_load_only_cud = null;
205+
200206
@IgnoreForAnnotationCheck
201207
private Set<LOG_LEVEL> allowedLogLevels = null;
202208

@@ -254,6 +260,10 @@ public String getBasePath() {
254260
return base_path;
255261
}
256262

263+
public String getSuperTokensLoadOnlyCUD() {
264+
return supertokens_saas_load_only_cud;
265+
}
266+
257267
public enum PASSWORD_HASHING_ALG {
258268
ARGON2, BCRYPT, FIREBASE_SCRYPT
259269
}
@@ -663,6 +673,15 @@ void normalizeAndValidate(Main main) throws InvalidConfigException {
663673
host = cliHost;
664674
}
665675

676+
if (supertokens_saas_load_only_cud != null) {
677+
try {
678+
supertokens_saas_load_only_cud =
679+
Utils.normalizeAndValidateConnectionUriDomain(supertokens_saas_load_only_cud, true);
680+
} catch (ServletException e) {
681+
throw new InvalidConfigException("supertokens_saas_load_only_cud is invalid");
682+
}
683+
}
684+
666685
access_token_validity = access_token_validity * 1000;
667686
access_token_dynamic_signing_key_update_interval = access_token_dynamic_signing_key_update_interval * 3600 * 1000;
668687
refresh_token_validity = refresh_token_validity * 60 * 1000;

src/main/java/io/supertokens/multitenancy/MultitenancyHelper.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,20 @@ public class MultitenancyHelper extends ResourceDistributor.SingletonResource {
5151
private Main main;
5252
private TenantConfig[] tenantConfigs;
5353

54+
// when the core has `supertokens_saas_load_only_cud` set, the tenantConfigs array will be filtered
55+
// based on the config value. However, we need to keep all the list of CUDs from the db to be able
56+
// to check if the CUD is present in the DB or not, while processing the requests.
57+
private final Set<String> dangerous_allCUDsFromDb = new HashSet<>();
58+
5459
private MultitenancyHelper(Main main) throws StorageQueryException {
5560
this.main = main;
56-
this.tenantConfigs = getAllTenantsFromDb();
61+
TenantConfig[] allTenantsFromDb = getAllTenantsFromDb();
62+
this.tenantConfigs = this.getFilteredTenantConfigs(allTenantsFromDb);
63+
this.dangerous_allCUDsFromDb.clear();
64+
65+
for (TenantConfig config : allTenantsFromDb) {
66+
this.dangerous_allCUDsFromDb.add(config.tenantIdentifier.getConnectionUriDomain());
67+
}
5768
}
5869

5970
public static MultitenancyHelper getInstance(Main main) {
@@ -81,7 +92,7 @@ public static void init(Main main) throws StorageQueryException, IOException {
8192
// instance of eeFeatureFlag. This is applicable only when the core is starting on
8293
// an empty database as no tenants are loaded from the db yet.
8394
} catch (CannotModifyBaseConfigException | BadPermissionException | FeatureNotEnabledException |
84-
InvalidConfigException | InvalidProviderConfigException | TenantOrAppNotFoundException e) {
95+
InvalidConfigException | InvalidProviderConfigException | TenantOrAppNotFoundException e) {
8596
throw new IllegalStateException(e);
8697
}
8798
}
@@ -108,10 +119,11 @@ public List<TenantIdentifier> refreshTenantsInCoreBasedOnChangesInCoreConfigOrIf
108119
return main.getResourceDistributor().withResourceDistributorLock(() -> {
109120
try {
110121
TenantConfig[] tenantsFromDb = getAllTenantsFromDb();
122+
TenantConfig[] filteredTenantsFromDb = this.getFilteredTenantConfigs(tenantsFromDb);
111123

112124
Map<ResourceDistributor.KeyClass, JsonObject> normalizedTenantsFromDb =
113125
Config.getNormalisedConfigsForAllTenants(
114-
tenantsFromDb, Config.getBaseConfigAsJsonObject(main));
126+
filteredTenantsFromDb, Config.getBaseConfigAsJsonObject(main));
115127

116128
Map<ResourceDistributor.KeyClass, JsonObject> normalizedTenantsFromMemory =
117129
Config.getNormalisedConfigsForAllTenants(
@@ -129,9 +141,14 @@ public List<TenantIdentifier> refreshTenantsInCoreBasedOnChangesInCoreConfigOrIf
129141
}
130142
}
131143

132-
boolean sameNumberOfTenants = tenantsFromDb.length == this.tenantConfigs.length;
144+
boolean sameNumberOfTenants =
145+
filteredTenantsFromDb.length == this.tenantConfigs.length;
133146

134-
this.tenantConfigs = tenantsFromDb;
147+
this.dangerous_allCUDsFromDb.clear();
148+
for (TenantConfig tenant : tenantsFromDb) {
149+
this.dangerous_allCUDsFromDb.add(tenant.tenantIdentifier.getConnectionUriDomain());
150+
}
151+
this.tenantConfigs = filteredTenantsFromDb;
135152
if (tenantsThatChanged.size() == 0 && sameNumberOfTenants) {
136153
return tenantsThatChanged;
137154
}
@@ -190,7 +207,7 @@ public void loadStorageLayer() throws IOException, InvalidConfigException {
190207
public void loadFeatureFlag(List<TenantIdentifier> tenantsThatChanged) {
191208
List<AppIdentifier> apps = new ArrayList<>();
192209
Set<AppIdentifier> appsSet = new HashSet<>();
193-
for (TenantConfig t : tenantConfigs) {
210+
for (TenantConfig t : this.tenantConfigs) {
194211
if (appsSet.contains(t.tenantIdentifier.toAppIdentifier())) {
195212
continue;
196213
}
@@ -204,7 +221,7 @@ public void loadSigningKeys(List<TenantIdentifier> tenantsThatChanged)
204221
throws UnsupportedJWTSigningAlgorithmException {
205222
List<AppIdentifier> apps = new ArrayList<>();
206223
Set<AppIdentifier> appsSet = new HashSet<>();
207-
for (TenantConfig t : tenantConfigs) {
224+
for (TenantConfig t : this.tenantConfigs) {
208225
if (appsSet.contains(t.tenantIdentifier.toAppIdentifier())) {
209226
continue;
210227
}
@@ -238,4 +255,21 @@ public TenantConfig[] getAllTenants() {
238255
throw new IllegalStateException(e);
239256
}
240257
}
241-
}
258+
259+
private TenantConfig[] getFilteredTenantConfigs(TenantConfig[] inputTenantConfigs) {
260+
String loadOnlyCUD = Config.getBaseConfig(main).getSuperTokensLoadOnlyCUD();
261+
262+
if (loadOnlyCUD == null) {
263+
return inputTenantConfigs;
264+
}
265+
266+
return Arrays.stream(inputTenantConfigs)
267+
.filter(tenantConfig -> tenantConfig.tenantIdentifier.getConnectionUriDomain().equals(loadOnlyCUD)
268+
|| tenantConfig.tenantIdentifier.getConnectionUriDomain().equals(TenantIdentifier.DEFAULT_CONNECTION_URI))
269+
.toArray(TenantConfig[]::new);
270+
}
271+
272+
public boolean isConnectionUriDomainPresentInDb(String cud) {
273+
return this.dangerous_allCUDsFromDb.contains(cud);
274+
}
275+
}

src/main/java/io/supertokens/storageLayer/StorageLayer.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ public static void loadAllTenantStorage(Main main, TenantConfig[] tenants)
242242
}
243243
main.getResourceDistributor().clearAllResourcesWithResourceKey(RESOURCE_KEY);
244244

245-
Set<String> userPoolsInUse = new HashSet<>();
245+
Set<String> uniquePoolsInUse = new HashSet<>();
246246

247247
for (ResourceDistributor.KeyClass key : resourceKeyToStorageMap.keySet()) {
248248
Storage currStorage = resourceKeyToStorageMap.get(key);
@@ -259,11 +259,16 @@ public static void loadAllTenantStorage(Main main, TenantConfig[] tenants)
259259
main.getResourceDistributor().setResource(key.getTenantIdentifier(), RESOURCE_KEY,
260260
new StorageLayer(resourceKeyToStorageMap.get(key)));
261261

262-
userPoolsInUse.add(userPoolId);
262+
uniquePoolsInUse.add(uniqueId);
263263
}
264264

265265
for (ResourceDistributor.KeyClass key : existingStorageMap.keySet()) {
266-
if (!userPoolsInUse.contains(((StorageLayer) existingStorageMap.get(key)).storage.getUserPoolId())) {
266+
Storage existingStorage = ((StorageLayer) existingStorageMap.get(key)).storage;
267+
String userPoolId = existingStorage.getUserPoolId();
268+
String connectionPoolId = existingStorage.getConnectionPoolId();
269+
String uniqueId = userPoolId + "~" + connectionPoolId;
270+
271+
if (!uniquePoolsInUse.contains(uniqueId)) {
267272
((StorageLayer) existingStorageMap.get(key)).storage.close();
268273
((StorageLayer) existingStorageMap.get(key)).storage.stopLogging();
269274
}

src/main/java/io/supertokens/webserver/WebserverAPI.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.supertokens.config.CoreConfig;
2525
import io.supertokens.exceptions.QuitProgramException;
2626
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
27+
import io.supertokens.multitenancy.MultitenancyHelper;
2728
import io.supertokens.multitenancy.exception.BadPermissionException;
2829
import io.supertokens.output.Logging;
2930
import io.supertokens.pluginInterface.Storage;
@@ -286,15 +287,18 @@ private String getConnectionUriDomain(HttpServletRequest req) throws ServletExce
286287
String connectionUriDomain = req.getServerName();
287288
connectionUriDomain = Utils.normalizeAndValidateConnectionUriDomain(connectionUriDomain, false);
288289

289-
try {
290-
if (Config.getConfig(new TenantIdentifier(connectionUriDomain, null, null), main) ==
291-
Config.getConfig(new TenantIdentifier(null, null, null), main)) {
292-
return null;
290+
if (MultitenancyHelper.getInstance(main).isConnectionUriDomainPresentInDb(connectionUriDomain)) {
291+
CoreConfig baseConfig = Config.getBaseConfig(main);
292+
if (baseConfig.getSuperTokensLoadOnlyCUD() != null) {
293+
if (!connectionUriDomain.equals(baseConfig.getSuperTokensLoadOnlyCUD())) {
294+
throw new ServletException(new BadRequestException("Connection URI domain is disallowed"));
295+
}
293296
}
294-
} catch (TenantOrAppNotFoundException e) {
295-
throw new IllegalStateException(e);
297+
298+
return connectionUriDomain;
296299
}
297-
return connectionUriDomain;
300+
301+
return null;
298302
}
299303

300304
@TestOnly
@@ -480,10 +484,12 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws
480484
}
481485
Logging.info(main, tenantIdentifier, "API ended: " + req.getRequestURI() + ". Method: " + req.getMethod(),
482486
false);
483-
try {
484-
RequestStats.getInstance(main, tenantIdentifier.toAppIdentifier()).updateRequestStats();
485-
} catch (TenantOrAppNotFoundException e) {
486-
// Ignore the error as we would have already sent the response for tenantNotFound
487+
if (tenantIdentifier != null) {
488+
try {
489+
RequestStats.getInstance(main, tenantIdentifier.toAppIdentifier()).updateRequestStats();
490+
} catch (TenantOrAppNotFoundException e) {
491+
// Ignore the error as we would have already sent the response for tenantNotFound
492+
}
487493
}
488494
}
489495

src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.google.gson.JsonElement;
2020
import com.google.gson.JsonObject;
2121
import io.supertokens.Main;
22+
import io.supertokens.config.Config;
23+
import io.supertokens.config.CoreConfig;
2224
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
2325
import io.supertokens.multitenancy.Multitenancy;
2426
import io.supertokens.multitenancy.exception.BadPermissionException;
@@ -49,6 +51,14 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent
4951
HttpServletResponse resp)
5052
throws ServletException, IOException {
5153

54+
CoreConfig baseConfig = Config.getBaseConfig(main);
55+
if (baseConfig.getSuperTokensLoadOnlyCUD() != null) {
56+
if (!(targetTenantIdentifier.getConnectionUriDomain().equals(TenantIdentifier.DEFAULT_CONNECTION_URI) || targetTenantIdentifier.getConnectionUriDomain().equals(baseConfig.getSuperTokensLoadOnlyCUD()))) {
57+
throw new ServletException(new BadRequestException("Creation of connection uri domain or app or " +
58+
"tenant is disallowed"));
59+
}
60+
}
61+
5262
TenantConfig tenantConfig = Multitenancy.getTenantInfo(main,
5363
new TenantIdentifier(targetTenantIdentifier.getConnectionUriDomain(), targetTenantIdentifier.getAppId(),
5464
targetTenantIdentifier.getTenantId()));

src/test/java/io/supertokens/test/PathRouterTest.java

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,9 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
15331533
public void tenantNotFoundTest3()
15341534
throws InterruptedException, IOException, io.supertokens.httpRequest.HttpResponseException,
15351535
InvalidConfigException,
1536-
io.supertokens.test.httpRequest.HttpResponseException, TenantOrAppNotFoundException {
1536+
io.supertokens.test.httpRequest.HttpResponseException, TenantOrAppNotFoundException,
1537+
InvalidProviderConfigException, StorageQueryException, FeatureNotEnabledException,
1538+
CannotModifyBaseConfigException, BadPermissionException {
15371539
String[] args = {"../"};
15381540

15391541
Utils.setValueInConfig("host", "\"0.0.0.0\"");
@@ -1556,15 +1558,26 @@ public void tenantNotFoundTest3()
15561558
StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess())
15571559
.modifyConfigToAddANewUserPoolForTesting(tenantConfig, 2);
15581560

1559-
Config.loadAllTenantConfig(process.getProcess(), new TenantConfig[]{
1560-
new TenantConfig(new TenantIdentifier("localhost", null, null), new EmailPasswordConfig(false),
1561+
Multitenancy.addNewOrUpdateAppOrTenant(
1562+
process.getProcess(),
1563+
new TenantConfig(
1564+
new TenantIdentifier("localhost", null, null),
1565+
new EmailPasswordConfig(false),
15611566
new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
15621567
new PasswordlessConfig(false),
15631568
tenantConfig),
1564-
new TenantConfig(new TenantIdentifier("localhost", null, "t1"), new EmailPasswordConfig(false),
1569+
false
1570+
);
1571+
Multitenancy.addNewOrUpdateAppOrTenant(
1572+
process.getProcess(),
1573+
new TenantConfig(
1574+
new TenantIdentifier("localhost", null, "t1"),
1575+
new EmailPasswordConfig(false),
15651576
new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
15661577
new PasswordlessConfig(false),
1567-
tenantConfig)}, new ArrayList<>());
1578+
tenantConfig),
1579+
false
1580+
);
15681581

15691582
Webserver.getInstance(process.getProcess()).addAPI(new WebserverAPI(process.getProcess(), "") {
15701583

@@ -2788,7 +2801,9 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
27882801
public void tenantNotFoundWithAppIdTest3()
27892802
throws InterruptedException, IOException, io.supertokens.httpRequest.HttpResponseException,
27902803
InvalidConfigException,
2791-
io.supertokens.test.httpRequest.HttpResponseException, TenantOrAppNotFoundException {
2804+
io.supertokens.test.httpRequest.HttpResponseException, TenantOrAppNotFoundException,
2805+
InvalidProviderConfigException, StorageQueryException, FeatureNotEnabledException,
2806+
CannotModifyBaseConfigException, BadPermissionException {
27922807
String[] args = {"../"};
27932808

27942809
Utils.setValueInConfig("host", "\"0.0.0.0\"");
@@ -2811,15 +2826,26 @@ public void tenantNotFoundWithAppIdTest3()
28112826
StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess())
28122827
.modifyConfigToAddANewUserPoolForTesting(tenantConfig, 2);
28132828

2814-
Config.loadAllTenantConfig(process.getProcess(), new TenantConfig[]{
2815-
new TenantConfig(new TenantIdentifier("localhost", null, null), new EmailPasswordConfig(false),
2829+
Multitenancy.addNewOrUpdateAppOrTenant(
2830+
process.getProcess(),
2831+
new TenantConfig(
2832+
new TenantIdentifier("localhost", null, null),
2833+
new EmailPasswordConfig(false),
28162834
new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
28172835
new PasswordlessConfig(false),
28182836
tenantConfig),
2819-
new TenantConfig(new TenantIdentifier("localhost", "app1", "t1"), new EmailPasswordConfig(false),
2837+
false
2838+
);
2839+
Multitenancy.addNewOrUpdateAppOrTenant(
2840+
process.getProcess(),
2841+
new TenantConfig(
2842+
new TenantIdentifier("localhost", "app1", "t1"),
2843+
new EmailPasswordConfig(false),
28202844
new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
28212845
new PasswordlessConfig(false),
2822-
tenantConfig)}, new ArrayList<>());
2846+
tenantConfig),
2847+
false
2848+
);
28232849

28242850
Webserver.getInstance(process.getProcess()).addAPI(new WebserverAPI(process.getProcess(), "") {
28252851

@@ -2875,4 +2901,4 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
28752901
process.kill();
28762902
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
28772903
}
2878-
}
2904+
}

0 commit comments

Comments
 (0)