From ff6237dc2d6fcb8741da9af6d047286193056962 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 4 Dec 2023 18:57:51 +0530 Subject: [PATCH 01/10] fix: core config validation --- CHANGELOG.md | 4 ++++ build.gradle | 2 +- .../api/multitenancy/BaseCreateOrUpdate.java | 3 +++ .../test/multitenant/api/TestApp.java | 22 +++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1577e75ef..fb1f11ec2 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.16] - 2023-12-04 + +- Returns 400, instead of 500, for badly typed core config while create CUD, App or Tenant + ## [7.0.15] - 2023-11-28 - Adds test for user pagination from old version diff --git a/build.gradle b/build.gradle index 7d494180f..9e3e4795c 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" } // } //} -version = "7.0.15" +version = "7.0.16" repositories { diff --git a/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java b/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java index ac930fb7b..bf0318375 100644 --- a/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java +++ b/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java @@ -16,6 +16,7 @@ package io.supertokens.webserver.api.multitenancy; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.supertokens.Main; @@ -138,6 +139,8 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent throw new ServletException(new BadRequestException("Invalid core config: " + e.getMessage())); } catch (InvalidProviderConfigException e) { throw new ServletException(new BadRequestException("Invalid third party config: " + e.getMessage())); + } catch (InvalidFormatException e) { + throw new ServletException(new BadRequestException("Cannot deserialize value `" + e.getValue() + "` for property `" + e.getPath().get(0).getFieldName() + "`")); } } diff --git a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java index fee7b1ad8..99ccc6757 100644 --- a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java +++ b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java @@ -507,4 +507,26 @@ public void testDefaultRecipesEnabledWhileCreatingApp() throws Exception { assertTrue(tenant.get("thirdParty").getAsJsonObject().get("enabled").getAsBoolean()); assertTrue(tenant.get("passwordless").getAsJsonObject().get("enabled").getAsBoolean()); } + + @Test + public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception { + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + + JsonObject config = new JsonObject(); + config.addProperty("access_token_validity", "abcd"); + StorageLayer.getBaseStorage(process.getProcess()).modifyConfigToAddANewUserPoolForTesting(config, 1); + + try { + JsonObject response = TestMultitenancyAPIHelper.createApp( + process.getProcess(), + new TenantIdentifier(null, null, null), + "a1", null, null, null, + config); + } catch (HttpResponseException e) { + assertEquals(400, e.statusCode); + assertTrue(e.getMessage().contains("Cannot deserialize value `abcd` for property `access_token_validity`")); + } + } } From 440bc3bcfd18624fc9a9c5955f34467ab64ca881 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 4 Dec 2023 19:00:16 +0530 Subject: [PATCH 02/10] fix: core config validation --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb1f11ec2..14dbff8fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [7.0.16] - 2023-12-04 -- Returns 400, instead of 500, for badly typed core config while create CUD, App or Tenant +- Returns 400, instead of 500, for badly typed core config while creating CUD, App or Tenant ## [7.0.15] - 2023-11-28 From 5138690634d285208b66b89a6ea6429e3cd4434a Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 5 Dec 2023 19:39:27 +0530 Subject: [PATCH 03/10] fix: PR comments --- .../java/io/supertokens/config/Config.java | 14 ++++- .../api/multitenancy/BaseCreateOrUpdate.java | 3 - .../test/multitenant/api/TestApp.java | 59 +++++++++++++++---- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/supertokens/config/Config.java b/src/main/java/io/supertokens/config/Config.java index 042af8c9f..a818373ea 100644 --- a/src/main/java/io/supertokens/config/Config.java +++ b/src/main/java/io/supertokens/config/Config.java @@ -17,6 +17,7 @@ package io.supertokens.config; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -57,9 +58,16 @@ private Config(Main main, String configFilePath) throws InvalidConfigException, private Config(Main main, JsonObject jsonConfig) throws IOException, InvalidConfigException { this.main = main; final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - CoreConfig config = mapper.readValue(jsonConfig.toString(), CoreConfig.class); - config.normalizeAndValidate(main); - this.core = config; + try { + CoreConfig config = mapper.readValue(jsonConfig.toString(), CoreConfig.class); + config.normalizeAndValidate(main); + this.core = config; + } catch (InvalidFormatException e) { + throw new InvalidConfigException( + "Cannot set value " + e.getValue().toString() + " for field " + e.getPath().get(0).getFieldName() + + " of type " + e.getTargetType().getSimpleName() + ); + } } public static Config getInstance(TenantIdentifier tenantIdentifier, Main main) diff --git a/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java b/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java index bf0318375..ac930fb7b 100644 --- a/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java +++ b/src/main/java/io/supertokens/webserver/api/multitenancy/BaseCreateOrUpdate.java @@ -16,7 +16,6 @@ package io.supertokens.webserver.api.multitenancy; -import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.supertokens.Main; @@ -139,8 +138,6 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent throw new ServletException(new BadRequestException("Invalid core config: " + e.getMessage())); } catch (InvalidProviderConfigException e) { throw new ServletException(new BadRequestException("Invalid third party config: " + e.getMessage())); - } catch (InvalidFormatException e) { - throw new ServletException(new BadRequestException("Cannot deserialize value `" + e.getValue() + "` for property `" + e.getPath().get(0).getFieldName() + "`")); } } diff --git a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java index 99ccc6757..f2caae5ea 100644 --- a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java +++ b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java @@ -514,19 +514,54 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception return; } - JsonObject config = new JsonObject(); - config.addProperty("access_token_validity", "abcd"); - StorageLayer.getBaseStorage(process.getProcess()).modifyConfigToAddANewUserPoolForTesting(config, 1); + if (StorageLayer.isInMemDb(process.getProcess())) { + return; + } - try { - JsonObject response = TestMultitenancyAPIHelper.createApp( - process.getProcess(), - new TenantIdentifier(null, null, null), - "a1", null, null, null, - config); - } catch (HttpResponseException e) { - assertEquals(400, e.statusCode); - assertTrue(e.getMessage().contains("Cannot deserialize value `abcd` for property `access_token_validity`")); + String[] properties = new String[]{ + "access_token_validity", // long + "disable_telemetry", // boolean + "postgresql_connection_pool_size", // int + "mysql_connection_pool_size", // int + }; + Object[] values = new Object[]{ + "abcd", // access_token_validity + "abcd", // disable_telemetry + "abcd", // postgresql_connection_pool_size + "abcd", // mysql_connection_pool_size + }; + + System.out.println(StorageLayer.getStorage(process.getProcess()).getClass().getCanonicalName()); + + for (int i = 0; i < properties.length; i++) { + try { + System.out.println("Test case " + i); + JsonObject config = new JsonObject(); + if (values[i] instanceof String) { + config.addProperty(properties[i], (String) values[i]); + } else if (values[i] instanceof Boolean) { + config.addProperty(properties[i], (Boolean) values[i]); + } else if (values[i] instanceof Number) { + config.addProperty(properties[i], (Number) values[i]); + } else { + throw new RuntimeException("Invalid type"); + } + StorageLayer.getBaseStorage(process.getProcess()).modifyConfigToAddANewUserPoolForTesting(config, 1); + + JsonObject response = TestMultitenancyAPIHelper.createApp( + process.getProcess(), + new TenantIdentifier(null, null, null), + "a1", null, null, null, + config); + fail(); + } catch (HttpResponseException e) { + System.out.println(e.getMessage()); + assertEquals(400, e.statusCode); + if (!e.getMessage().contains("Invalid config key")) { + assertTrue(e.getMessage().contains("Cannot set value")); + assertTrue(e.getMessage().contains("for field " + properties[i])); + } + } } } } From 76dfddbf2ed6ed8ff6f0931cb17f445c32673983 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 6 Dec 2023 11:25:24 +0530 Subject: [PATCH 04/10] fix: PR comments --- src/main/java/io/supertokens/config/Config.java | 15 +++++++++------ .../supertokens/test/multitenant/api/TestApp.java | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/supertokens/config/Config.java b/src/main/java/io/supertokens/config/Config.java index a818373ea..72af74478 100644 --- a/src/main/java/io/supertokens/config/Config.java +++ b/src/main/java/io/supertokens/config/Config.java @@ -50,9 +50,14 @@ public class Config extends ResourceDistributor.SingletonResource { private Config(Main main, String configFilePath) throws InvalidConfigException, IOException { this.main = main; final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - CoreConfig config = mapper.readValue(new File(configFilePath), CoreConfig.class); - config.normalizeAndValidate(main); - this.core = config; + try { + CoreConfig config = mapper.readValue(new File(configFilePath), CoreConfig.class); + config.normalizeAndValidate(main); + this.core = config; + } catch (InvalidFormatException e) { + throw new InvalidConfigException( + "'" + e.getPath().get(0).getFieldName() + "' must be of type " + e.getTargetType().getSimpleName()); + } } private Config(Main main, JsonObject jsonConfig) throws IOException, InvalidConfigException { @@ -64,9 +69,7 @@ private Config(Main main, JsonObject jsonConfig) throws IOException, InvalidConf this.core = config; } catch (InvalidFormatException e) { throw new InvalidConfigException( - "Cannot set value " + e.getValue().toString() + " for field " + e.getPath().get(0).getFieldName() - + " of type " + e.getTargetType().getSimpleName() - ); + "'" + e.getPath().get(0).getFieldName() + "' must be of type " + e.getTargetType().getSimpleName()); } } diff --git a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java index f2caae5ea..5ea74cd94 100644 --- a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java +++ b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java @@ -558,8 +558,8 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception System.out.println(e.getMessage()); assertEquals(400, e.statusCode); if (!e.getMessage().contains("Invalid config key")) { - assertTrue(e.getMessage().contains("Cannot set value")); - assertTrue(e.getMessage().contains("for field " + properties[i])); + assertTrue(e.getMessage().contains("must be of type")); + assertTrue(e.getMessage().contains(properties[i])); } } } From 748307dcb3e239ccfa7993954645171a10997231 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 6 Dec 2023 11:45:53 +0530 Subject: [PATCH 05/10] fix: test --- .../io/supertokens/test/multitenant/api/TestApp.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java index 5ea74cd94..6270ca701 100644 --- a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java +++ b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java @@ -531,6 +531,13 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception "abcd", // mysql_connection_pool_size }; + String[] expectedErrorMessages = new String[]{ + "Http error. Status Code: 400. Message: Invalid core config: 'access_token_validity' must be of type long", // access_token_validity + "Http error. Status Code: 400. Message: Invalid core config: 'disable_telemetry' must be of type boolean", // disable_telemetry + "Http error. Status Code: 400. Message: Invalid core config: 'postgresql_connection_pool_size' must be of type int", // postgresql_connection_pool_size + "Http error. Status Code: 400. Message: Invalid core config: 'mysql_connection_pool_size' must be of type int", // mysql_connection_pool_size + }; + System.out.println(StorageLayer.getStorage(process.getProcess()).getClass().getCanonicalName()); for (int i = 0; i < properties.length; i++) { @@ -555,11 +562,9 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception config); fail(); } catch (HttpResponseException e) { - System.out.println(e.getMessage()); assertEquals(400, e.statusCode); if (!e.getMessage().contains("Invalid config key")) { - assertTrue(e.getMessage().contains("must be of type")); - assertTrue(e.getMessage().contains(properties[i])); + assertEquals(expectedErrorMessages[i], e.getMessage()); } } } From 0c00922959f5129498cdbf6e0a94af36263b6642 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 6 Dec 2023 11:50:18 +0530 Subject: [PATCH 06/10] fix: startup test --- .../io/supertokens/test/ConfigTest2_21.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/io/supertokens/test/ConfigTest2_21.java b/src/test/java/io/supertokens/test/ConfigTest2_21.java index 73b886f6f..086cbd1bf 100644 --- a/src/test/java/io/supertokens/test/ConfigTest2_21.java +++ b/src/test/java/io/supertokens/test/ConfigTest2_21.java @@ -24,6 +24,7 @@ import org.junit.*; import org.junit.rules.TestRule; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class ConfigTest2_21 { @@ -84,4 +85,23 @@ public void testThatNewConfigWorks() throws Exception { EventAndException stopEvent = process.checkOrWaitForEvent(PROCESS_STATE.STOPPED); assertNotNull(stopEvent); } + + @Test + public void testCoreConfigTypeValidationInConfigYaml() throws Exception { + Utils.setValueInConfig("access_token_validity", "abcd"); + + String[] args = { "../" }; + + TestingProcess process = TestingProcessManager.start(args); + + EventAndException startEvent = process.checkOrWaitForEvent(PROCESS_STATE.INIT_FAILURE); + assertNotNull(startEvent); + + assertEquals("io.supertokens.pluginInterface.exceptions.InvalidConfigException: 'access_token_validity' must be of type long", startEvent.exception.getMessage()); + + process.kill(); + EventAndException stopEvent = process.checkOrWaitForEvent(PROCESS_STATE.STOPPED); + assertNotNull(stopEvent); + + } } From e6125a4821664e5d02a8a05fdb834389903c33ff Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 6 Dec 2023 17:10:23 +0530 Subject: [PATCH 07/10] fix: using ConfigMapper --- .../java/io/supertokens/config/Config.java | 26 ++-- .../io/supertokens/test/ConfigMapperTest.java | 126 ++++++++++++++++++ .../io/supertokens/test/ConfigTest2_21.java | 1 - .../test/multitenant/api/TestApp.java | 19 ++- 4 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 src/test/java/io/supertokens/test/ConfigMapperTest.java diff --git a/src/main/java/io/supertokens/config/Config.java b/src/main/java/io/supertokens/config/Config.java index 72af74478..17ac03eeb 100644 --- a/src/main/java/io/supertokens/config/Config.java +++ b/src/main/java/io/supertokens/config/Config.java @@ -17,7 +17,6 @@ package io.supertokens.config; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -32,6 +31,7 @@ import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.storageLayer.StorageLayer; +import io.supertokens.pluginInterface.utils.ConfigMapper; import org.jetbrains.annotations.TestOnly; import java.io.File; @@ -50,27 +50,19 @@ public class Config extends ResourceDistributor.SingletonResource { private Config(Main main, String configFilePath) throws InvalidConfigException, IOException { this.main = main; final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - try { - CoreConfig config = mapper.readValue(new File(configFilePath), CoreConfig.class); - config.normalizeAndValidate(main); - this.core = config; - } catch (InvalidFormatException e) { - throw new InvalidConfigException( - "'" + e.getPath().get(0).getFieldName() + "' must be of type " + e.getTargetType().getSimpleName()); - } + Object configObj = mapper.readValue(new File(configFilePath), Object.class); + JsonObject jsonConfig = new Gson().toJsonTree(configObj).getAsJsonObject(); + CoreConfig config = ConfigMapper.mapConfig(jsonConfig, CoreConfig.class); + config.normalizeAndValidate(main); + this.core = config; } private Config(Main main, JsonObject jsonConfig) throws IOException, InvalidConfigException { this.main = main; final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - try { - CoreConfig config = mapper.readValue(jsonConfig.toString(), CoreConfig.class); - config.normalizeAndValidate(main); - this.core = config; - } catch (InvalidFormatException e) { - throw new InvalidConfigException( - "'" + e.getPath().get(0).getFieldName() + "' must be of type " + e.getTargetType().getSimpleName()); - } + CoreConfig config = ConfigMapper.mapConfig(jsonConfig, CoreConfig.class); + config.normalizeAndValidate(main); + this.core = config; } public static Config getInstance(TenantIdentifier tenantIdentifier, Main main) diff --git a/src/test/java/io/supertokens/test/ConfigMapperTest.java b/src/test/java/io/supertokens/test/ConfigMapperTest.java new file mode 100644 index 000000000..7bd5c5b2a --- /dev/null +++ b/src/test/java/io/supertokens/test/ConfigMapperTest.java @@ -0,0 +1,126 @@ +/* + * 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.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.JsonObject; +import io.supertokens.pluginInterface.utils.ConfigMapper; +import org.junit.Assert; +import org.junit.Test; + +public class ConfigMapperTest { + + public static class DummyConfig { + @JsonProperty + int int_property; + + @JsonProperty + long long_property; + + @JsonProperty + String string_property; + + @JsonProperty + boolean bool_property; + } + + @Test + public void testAllValidConversions() throws Exception { + // valid for int + { + JsonObject config = new JsonObject(); + config.addProperty("int_property", "100"); + Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).int_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("int_property", 100); + Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).int_property); + } + + // valid for long + { + JsonObject config = new JsonObject(); + config.addProperty("long_property", "100"); + Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).long_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("long_property", 100); + Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).long_property); + } + + // valid for bool + { + JsonObject config = new JsonObject(); + config.addProperty("bool_property", "true"); + Assert.assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("bool_property", "TRUE"); + Assert.assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("bool_property", "false"); + Assert.assertEquals(false, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("bool_property", true); + Assert.assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("bool_property", false); + Assert.assertEquals(false, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + } + + // valid for string + { + JsonObject config = new JsonObject(); + config.addProperty("string_property", "true"); + Assert.assertEquals("true", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("string_property", true); + Assert.assertEquals("true", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("string_property", 100); + Assert.assertEquals("100", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("string_property", 3.14); + Assert.assertEquals("3.14", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + } + { + JsonObject config = new JsonObject(); + config.addProperty("string_property", "hello"); + Assert.assertEquals("hello", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + } + } + + @Test + public void testInvalidConversions() throws Exception { + + } +} diff --git a/src/test/java/io/supertokens/test/ConfigTest2_21.java b/src/test/java/io/supertokens/test/ConfigTest2_21.java index 086cbd1bf..a4b28c060 100644 --- a/src/test/java/io/supertokens/test/ConfigTest2_21.java +++ b/src/test/java/io/supertokens/test/ConfigTest2_21.java @@ -102,6 +102,5 @@ public void testCoreConfigTypeValidationInConfigYaml() throws Exception { process.kill(); EventAndException stopEvent = process.checkOrWaitForEvent(PROCESS_STATE.STOPPED); assertNotNull(stopEvent); - } } diff --git a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java index 6270ca701..bdc483ab9 100644 --- a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java +++ b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java @@ -17,6 +17,7 @@ package io.supertokens.test.multitenant.api; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import io.supertokens.ProcessState; import io.supertokens.featureflag.EE_FEATURES; @@ -519,6 +520,9 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception } String[] properties = new String[]{ + "access_token_validity", // long + "access_token_validity", // long + "access_token_validity", // long "access_token_validity", // long "disable_telemetry", // boolean "postgresql_connection_pool_size", // int @@ -526,6 +530,9 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception }; Object[] values = new Object[]{ "abcd", // access_token_validity + "", + "null", + null, "abcd", // disable_telemetry "abcd", // postgresql_connection_pool_size "abcd", // mysql_connection_pool_size @@ -533,6 +540,9 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception String[] expectedErrorMessages = new String[]{ "Http error. Status Code: 400. Message: Invalid core config: 'access_token_validity' must be of type long", // access_token_validity + "Http error. Status Code: 400. Message: Invalid core config: 'access_token_validity' must be of type long", // access_token_validity + "Http error. Status Code: 400. Message: Invalid core config: 'access_token_validity' must be of type long", // access_token_validity + null, "Http error. Status Code: 400. Message: Invalid core config: 'disable_telemetry' must be of type boolean", // disable_telemetry "Http error. Status Code: 400. Message: Invalid core config: 'postgresql_connection_pool_size' must be of type int", // postgresql_connection_pool_size "Http error. Status Code: 400. Message: Invalid core config: 'mysql_connection_pool_size' must be of type int", // mysql_connection_pool_size @@ -544,7 +554,10 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception try { System.out.println("Test case " + i); JsonObject config = new JsonObject(); - if (values[i] instanceof String) { + if (values[i] == null) { + config.add(properties[i], null); + } + else if (values[i] instanceof String) { config.addProperty(properties[i], (String) values[i]); } else if (values[i] instanceof Boolean) { config.addProperty(properties[i], (Boolean) values[i]); @@ -560,7 +573,9 @@ public void testInvalidTypedValueInCoreConfigWhileCreatingApp() throws Exception new TenantIdentifier(null, null, null), "a1", null, null, null, config); - fail(); + if (expectedErrorMessages[i] != null) { + fail(); + } } catch (HttpResponseException e) { assertEquals(400, e.statusCode); if (!e.getMessage().contains("Invalid config key")) { From 9a75157843f5aff01e915ed161ae3035942095d6 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 6 Dec 2023 17:34:48 +0530 Subject: [PATCH 08/10] fix: test --- .../io/supertokens/test/ConfigMapperTest.java | 167 ++++++++++++++++-- 1 file changed, 152 insertions(+), 15 deletions(-) diff --git a/src/test/java/io/supertokens/test/ConfigMapperTest.java b/src/test/java/io/supertokens/test/ConfigMapperTest.java index 7bd5c5b2a..48d8c0bb5 100644 --- a/src/test/java/io/supertokens/test/ConfigMapperTest.java +++ b/src/test/java/io/supertokens/test/ConfigMapperTest.java @@ -18,10 +18,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.gson.JsonObject; +import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.pluginInterface.utils.ConfigMapper; -import org.junit.Assert; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + public class ConfigMapperTest { public static class DummyConfig { @@ -31,6 +34,12 @@ public static class DummyConfig { @JsonProperty long long_property; + @JsonProperty + float float_property; + + @JsonProperty + double double_property; + @JsonProperty String string_property; @@ -44,83 +53,211 @@ public void testAllValidConversions() throws Exception { { JsonObject config = new JsonObject(); config.addProperty("int_property", "100"); - Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).int_property); + assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).int_property); } { JsonObject config = new JsonObject(); config.addProperty("int_property", 100); - Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).int_property); + assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).int_property); } // valid for long { JsonObject config = new JsonObject(); config.addProperty("long_property", "100"); - Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).long_property); + assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).long_property); } { JsonObject config = new JsonObject(); config.addProperty("long_property", 100); - Assert.assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).long_property); + assertEquals(100, ConfigMapper.mapConfig(config, DummyConfig.class).long_property); + } + + // valid for float + { + JsonObject config = new JsonObject(); + config.addProperty("float_property", 100); + System.out.println(ConfigMapper.mapConfig(config, DummyConfig.class).float_property); + assertEquals((float) 100, ConfigMapper.mapConfig(config, DummyConfig.class).float_property, 0.001); + } + { + JsonObject config = new JsonObject(); + config.addProperty("float_property", 3.14); + assertEquals((float) 3.14, ConfigMapper.mapConfig(config, DummyConfig.class).float_property, 0.001); + } + { + JsonObject config = new JsonObject(); + config.addProperty("float_property", "100"); + assertEquals((float) 100, ConfigMapper.mapConfig(config, DummyConfig.class).float_property, 0.001); + } + { + JsonObject config = new JsonObject(); + config.addProperty("float_property", "3.14"); + assertEquals((float) 3.14, ConfigMapper.mapConfig(config, DummyConfig.class).float_property, 0.001); + } + + // valid double + { + JsonObject config = new JsonObject(); + config.addProperty("double_property", 100); + assertEquals((double) 100, ConfigMapper.mapConfig(config, DummyConfig.class).double_property, 0.001); + } + { + JsonObject config = new JsonObject(); + config.addProperty("double_property", 3.14); + assertEquals((double) 3.14, ConfigMapper.mapConfig(config, DummyConfig.class).double_property, 0.001); + } + { + JsonObject config = new JsonObject(); + config.addProperty("double_property", "100"); + assertEquals((double) 100, ConfigMapper.mapConfig(config, DummyConfig.class).double_property, 0.001); + } + { + JsonObject config = new JsonObject(); + config.addProperty("double_property", "3.14"); + assertEquals((double) 3.14, ConfigMapper.mapConfig(config, DummyConfig.class).double_property, 0.001); } // valid for bool { JsonObject config = new JsonObject(); config.addProperty("bool_property", "true"); - Assert.assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); } { JsonObject config = new JsonObject(); config.addProperty("bool_property", "TRUE"); - Assert.assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); } { JsonObject config = new JsonObject(); config.addProperty("bool_property", "false"); - Assert.assertEquals(false, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + assertEquals(false, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); } { JsonObject config = new JsonObject(); config.addProperty("bool_property", true); - Assert.assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + assertEquals(true, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); } { JsonObject config = new JsonObject(); config.addProperty("bool_property", false); - Assert.assertEquals(false, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); + assertEquals(false, ConfigMapper.mapConfig(config, DummyConfig.class).bool_property); } // valid for string { JsonObject config = new JsonObject(); config.addProperty("string_property", "true"); - Assert.assertEquals("true", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + assertEquals("true", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); } { JsonObject config = new JsonObject(); config.addProperty("string_property", true); - Assert.assertEquals("true", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + assertEquals("true", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); } { JsonObject config = new JsonObject(); config.addProperty("string_property", 100); - Assert.assertEquals("100", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + assertEquals("100", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); } { JsonObject config = new JsonObject(); config.addProperty("string_property", 3.14); - Assert.assertEquals("3.14", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + assertEquals("3.14", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); } { JsonObject config = new JsonObject(); config.addProperty("string_property", "hello"); - Assert.assertEquals("hello", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); + assertEquals("hello", ConfigMapper.mapConfig(config, DummyConfig.class).string_property); } } @Test public void testInvalidConversions() throws Exception { + String[] properties = new String[]{ + "int_property", + "int_property", + "int_property", + "int_property", + "int_property", + + "long_property", + "long_property", + "long_property", + "long_property", + "float_property", + "float_property", + "float_property", + + "double_property", + "double_property", + "double_property", + }; + Object[] values = new Object[]{ + "abcd", // int + "", // int + true, // int + new Double(4.5), // int + new Long(1234567892342l), // int + + "abcd", // long + "", // long + true, // long + new Double(4.5), // long + + "abcd", // float + "", // float + true, // float + + "abcd", // double + "", // double + true, // double + }; + + String[] expectedErrorMessages = new String[]{ + "'int_property' must be of type int", // int + "'int_property' must be of type int", // int + "'int_property' must be of type int", // int + "'int_property' must be of type int", // int + "'int_property' must be of type int", // int + + "'long_property' must be of type long", // long + "'long_property' must be of type long", // long + "'long_property' must be of type long", // long + "'long_property' must be of type long", // long + + "'float_property' must be of type float", // float + "'float_property' must be of type float", // float + "'float_property' must be of type float", // float + + "'double_property' must be of type double", // double + "'double_property' must be of type double", // double + "'double_property' must be of type double", // double + }; + + for (int i = 0; i < properties.length; i++) { + try { + System.out.println("Test case " + i); + JsonObject config = new JsonObject(); + if (values[i] == null) { + config.add(properties[i], null); + } + else if (values[i] instanceof String) { + config.addProperty(properties[i], (String) values[i]); + } else if (values[i] instanceof Boolean) { + config.addProperty(properties[i], (Boolean) values[i]); + } else if (values[i] instanceof Number) { + config.addProperty(properties[i], (Number) values[i]); + } else { + throw new RuntimeException("Invalid type"); + } + DummyConfig dc = ConfigMapper.mapConfig(config, DummyConfig.class); + fail(); + } catch (InvalidConfigException e) { + assertEquals(expectedErrorMessages[i], e.getMessage()); + } + } } } From 7da51674ec2d1d8be9490d764a5a13254c42e0d7 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 6 Dec 2023 17:56:27 +0530 Subject: [PATCH 09/10] fix: config mapper --- .../java/io/supertokens/config/Config.java | 2 +- .../io/supertokens/utils/ConfigMapper.java | 148 ++++++++++++++++++ .../io/supertokens/test/ConfigMapperTest.java | 2 +- 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/supertokens/utils/ConfigMapper.java diff --git a/src/main/java/io/supertokens/config/Config.java b/src/main/java/io/supertokens/config/Config.java index 17ac03eeb..326b419fe 100644 --- a/src/main/java/io/supertokens/config/Config.java +++ b/src/main/java/io/supertokens/config/Config.java @@ -31,7 +31,7 @@ import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.storageLayer.StorageLayer; -import io.supertokens.pluginInterface.utils.ConfigMapper; +import io.supertokens.utils.ConfigMapper; import org.jetbrains.annotations.TestOnly; import java.io.File; diff --git a/src/main/java/io/supertokens/utils/ConfigMapper.java b/src/main/java/io/supertokens/utils/ConfigMapper.java new file mode 100644 index 000000000..6f636098a --- /dev/null +++ b/src/main/java/io/supertokens/utils/ConfigMapper.java @@ -0,0 +1,148 @@ +/* + * 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.utils; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonAlias; +import io.supertokens.pluginInterface.exceptions.InvalidConfigException; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Map; + +public class ConfigMapper { + public static T mapConfig(JsonObject config, Class clazz) throws InvalidConfigException { + try { + T result = clazz.newInstance(); + for (Map.Entry entry : config.entrySet()) { + Field field = findField(clazz, entry.getKey()); + if (field != null) { + setValue(result, field, entry.getValue()); + } + } + return result; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static Field findField(Class clazz, String key) { + Field[] fields = clazz.getDeclaredFields(); + + for (Field field : fields) { + if (field.getName().equals(key)) { + return field; + } + + // Check for JsonProperty annotation + JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class); + if (jsonProperty != null && jsonProperty.value().equals(key)) { + return field; + } + + // Check for JsonAlias annotation + JsonAlias jsonAlias = field.getAnnotation(JsonAlias.class); + if (jsonAlias != null) { + for (String alias : jsonAlias.value()) { + if (alias.equals(key)) { + return field; + } + } + } + } + + return null; // Field not found + } + + private static void setValue(T object, Field field, JsonElement value) throws InvalidConfigException { + boolean foundAnnotation = false; + for (Annotation a : field.getAnnotations()) { + if (a.toString().contains("JsonProperty")) { + foundAnnotation = true; + break; + } + } + + if (!foundAnnotation) { + return; + } + + field.setAccessible(true); + Object convertedValue = convertJsonElementToTargetType(value, field.getType(), field.getName()); + if (convertedValue != null) { + try { + field.set(object, convertedValue); + } catch (IllegalAccessException e) { + throw new IllegalStateException("should never happen"); + } + } + } + + private static Object convertJsonElementToTargetType(JsonElement value, Class targetType, String fieldName) + throws InvalidConfigException { + // If the value is JsonNull, return null for any type + if (value instanceof JsonNull || value == null) { + return null; + } + + try { + if (targetType == String.class) { + return value.getAsString(); + } else if (targetType == Integer.class || targetType == int.class) { + if (value.getAsDouble() == (double) value.getAsInt()) { + return value.getAsInt(); + } + } else if (targetType == Long.class || targetType == long.class) { + if (value.getAsDouble() == (double) value.getAsLong()) { + return value.getAsLong(); + } + } else if (targetType == Double.class || targetType == double.class) { + return value.getAsDouble(); + } else if (targetType == Float.class || targetType == float.class) { + return value.getAsFloat(); + } else if (targetType == Boolean.class || targetType == boolean.class) { + // Handle boolean conversion from strings like "true", "false" + return handleBooleanConversion(value, fieldName); + } + } catch (NumberFormatException e) { + // do nothing, will fall into InvalidConfigException + } + + // Throw an exception for unsupported conversions + throw new InvalidConfigException("'" + fieldName + "' must be of type " + targetType.getSimpleName()); + } + + private static Object handleBooleanConversion(JsonElement value, String fieldName) throws InvalidConfigException { + // Handle boolean conversion from strings like "true", "false" + if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) { + String stringValue = value.getAsString().toLowerCase(); + if (stringValue.equals("true")) { + return true; + } else if (stringValue.equals("false")) { + return false; + } + } else if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isBoolean()) { + return value.getAsBoolean(); + } + + // Throw an exception for unsupported conversions + throw new InvalidConfigException("'" + fieldName + "' must be of type boolean"); + } +} diff --git a/src/test/java/io/supertokens/test/ConfigMapperTest.java b/src/test/java/io/supertokens/test/ConfigMapperTest.java index 48d8c0bb5..dc4b44c82 100644 --- a/src/test/java/io/supertokens/test/ConfigMapperTest.java +++ b/src/test/java/io/supertokens/test/ConfigMapperTest.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.gson.JsonObject; import io.supertokens.pluginInterface.exceptions.InvalidConfigException; -import io.supertokens.pluginInterface.utils.ConfigMapper; +import io.supertokens.utils.ConfigMapper; import org.junit.Test; import static org.junit.Assert.assertEquals; From 53cbacb10f3e20331240a99549fa3999cc5e3469 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 6 Dec 2023 18:18:31 +0530 Subject: [PATCH 10/10] fix: core config --- .../java/io/supertokens/config/Config.java | 5 ++- .../io/supertokens/config/CoreConfig.java | 22 ++++++++----- .../test/multitenant/api/TestApp.java | 33 ++++++++++++++++--- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/supertokens/config/Config.java b/src/main/java/io/supertokens/config/Config.java index 326b419fe..038f36575 100644 --- a/src/main/java/io/supertokens/config/Config.java +++ b/src/main/java/io/supertokens/config/Config.java @@ -53,15 +53,14 @@ private Config(Main main, String configFilePath) throws InvalidConfigException, Object configObj = mapper.readValue(new File(configFilePath), Object.class); JsonObject jsonConfig = new Gson().toJsonTree(configObj).getAsJsonObject(); CoreConfig config = ConfigMapper.mapConfig(jsonConfig, CoreConfig.class); - config.normalizeAndValidate(main); + config.normalizeAndValidate(main, true); this.core = config; } private Config(Main main, JsonObject jsonConfig) throws IOException, InvalidConfigException { this.main = main; - final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); CoreConfig config = ConfigMapper.mapConfig(jsonConfig, CoreConfig.class); - config.normalizeAndValidate(main); + config.normalizeAndValidate(main, false); this.core = config; } diff --git a/src/main/java/io/supertokens/config/CoreConfig.java b/src/main/java/io/supertokens/config/CoreConfig.java index e02fadb4d..ba291a4b2 100644 --- a/src/main/java/io/supertokens/config/CoreConfig.java +++ b/src/main/java/io/supertokens/config/CoreConfig.java @@ -394,7 +394,7 @@ private String getConfigFileLocation(Main main) { : CLIOptions.get(main).getConfigFilePath()).getAbsolutePath(); } - void normalizeAndValidate(Main main) throws InvalidConfigException { + void normalizeAndValidate(Main main, boolean includeConfigFilePath) throws InvalidConfigException { if (isNormalizedAndValid) { return; } @@ -407,8 +407,9 @@ void normalizeAndValidate(Main main) throws InvalidConfigException { } if (access_token_validity < 1 || access_token_validity > 86400000) { throw new InvalidConfigException( - "'access_token_validity' must be between 1 and 86400000 seconds inclusive. The config file can be" - + " found here: " + getConfigFileLocation(main)); + "'access_token_validity' must be between 1 and 86400000 seconds inclusive." + + (includeConfigFilePath ? " The config file can be" + + " found here: " + getConfigFileLocation(main) : "")); } Boolean validityTesting = CoreConfigTestContent.getInstance(main) .getValue(CoreConfigTestContent.VALIDITY_TESTING); @@ -417,16 +418,18 @@ void normalizeAndValidate(Main main) throws InvalidConfigException { if ((refresh_token_validity * 60) <= access_token_validity) { if (!Main.isTesting || validityTesting) { throw new InvalidConfigException( - "'refresh_token_validity' must be strictly greater than 'access_token_validity'. The config " - + "file can be found here: " + getConfigFileLocation(main)); + "'refresh_token_validity' must be strictly greater than 'access_token_validity'." + + (includeConfigFilePath ? " The config file can be" + + " found here: " + getConfigFileLocation(main) : "")); } } if (!Main.isTesting || validityTesting) { // since in testing we make this really small if (access_token_dynamic_signing_key_update_interval < 1) { throw new InvalidConfigException( - "'access_token_dynamic_signing_key_update_interval' must be greater than, equal to 1 hour. The " - + "config file can be found here: " + getConfigFileLocation(main)); + "'access_token_dynamic_signing_key_update_interval' must be greater than, equal to 1 hour." + + (includeConfigFilePath ? " The config file can be" + + " found here: " + getConfigFileLocation(main) : "")); } } @@ -456,8 +459,9 @@ void normalizeAndValidate(Main main) throws InvalidConfigException { if (max_server_pool_size <= 0) { throw new InvalidConfigException( - "'max_server_pool_size' must be >= 1. The config file can be found here: " - + getConfigFileLocation(main)); + "'max_server_pool_size' must be >= 1." + + (includeConfigFilePath ? " The config file can be" + + " found here: " + getConfigFileLocation(main) : "")); } if (api_keys != null) { diff --git a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java index bdc483ab9..b548ebb4c 100644 --- a/src/test/java/io/supertokens/test/multitenant/api/TestApp.java +++ b/src/test/java/io/supertokens/test/multitenant/api/TestApp.java @@ -17,9 +17,9 @@ package io.supertokens.test.multitenant.api; import com.google.gson.JsonElement; -import com.google.gson.JsonNull; import com.google.gson.JsonObject; import io.supertokens.ProcessState; +import io.supertokens.config.CoreConfigTestContent; import io.supertokens.featureflag.EE_FEATURES; import io.supertokens.featureflag.FeatureFlagTestContent; import io.supertokens.featureflag.exceptions.FeatureNotEnabledException; @@ -32,13 +32,11 @@ import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.storageLayer.StorageLayer; -import io.supertokens.test.HttpRequestTest; import io.supertokens.test.TestingProcessManager; import io.supertokens.test.Utils; import io.supertokens.test.httpRequest.HttpRequestForTesting; import io.supertokens.test.httpRequest.HttpResponseException; import io.supertokens.thirdparty.InvalidProviderConfigException; -import io.supertokens.utils.SemVer; import io.supertokens.webserver.Webserver; import io.supertokens.webserver.WebserverAPI; import jakarta.servlet.ServletException; @@ -50,7 +48,6 @@ import org.junit.Test; import java.io.IOException; -import java.rmi.ServerException; import static org.junit.Assert.*; @@ -584,4 +581,32 @@ else if (values[i] instanceof String) { } } } + + @Test + public void testInvalidCoreConfig() throws Exception { + if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) { + return; + } + CoreConfigTestContent.getInstance(process.getProcess()).setKeyValue(CoreConfigTestContent.VALIDITY_TESTING, + true); + + { + JsonObject config = new JsonObject(); + config.addProperty("access_token_validity", 3600); + config.addProperty("refresh_token_validity", 3); + StorageLayer.getBaseStorage(process.getProcess()).modifyConfigToAddANewUserPoolForTesting(config, 1); + + try { + JsonObject response = TestMultitenancyAPIHelper.createApp( + process.getProcess(), + new TenantIdentifier(null, null, null), + "a1", null, null, null, + config); + fail(); + } catch (HttpResponseException e) { + assertEquals(400, e.statusCode); + assertEquals("Http error. Status Code: 400. Message: Invalid core config: 'refresh_token_validity' must be strictly greater than 'access_token_validity'.", e.getMessage()); + } + } + } }