diff --git a/config.yaml b/config.yaml index 81e2c1994..7d18147ff 100644 --- a/config.yaml +++ b/config.yaml @@ -142,6 +142,7 @@ core_config_version: 0 # their tenants. It only exposes that information when this key is used instead of the regular api_keys config. # supertokens_saas_secret: -# (OPTIONAL | Default: null). This is used when the core needs to assume a specific CDI version when CDI version is not -# specified in the request. When set to null, the core will assume the latest version of the CDI. +# (DIFFERENT_ACROSS_APPS | OPTIONAL | Default: null). This is used when the core needs to assume a specific CDI version +# when CDI version is not specified in the request. When set to null, the core will assume the latest version of the +# CDI. # supertokens_default_cdi_version: diff --git a/devConfig.yaml b/devConfig.yaml index 0026d823d..11c45c2c1 100644 --- a/devConfig.yaml +++ b/devConfig.yaml @@ -143,6 +143,7 @@ disable_telemetry: true # their tenants. It only exposes that information when this key is used instead of the regular api_keys config. # supertokens_saas_secret: -# (OPTIONAL | Default: null). This is used when the core needs to assume a specific CDI version when CDI version is not -# specified in the request. When set to null, the core will assume the latest version of the CDI. +# (DIFFERENT_ACROSS_APPS | OPTIONAL | Default: null). This is used when the core needs to assume a specific CDI version +# when CDI version is not specified in the request. When set to null, the core will assume the latest version of the +# CDI. # supertokens_default_cdi_version: diff --git a/src/main/java/io/supertokens/config/CoreConfig.java b/src/main/java/io/supertokens/config/CoreConfig.java index f2dc41d0f..a7dac33f0 100644 --- a/src/main/java/io/supertokens/config/CoreConfig.java +++ b/src/main/java/io/supertokens/config/CoreConfig.java @@ -187,7 +187,7 @@ public class CoreConfig { @JsonProperty private String supertokens_saas_secret = null; - @ConfigYamlOnly + @NotConflictingInApp @JsonProperty private String supertokens_default_cdi_version = null; diff --git a/src/main/java/io/supertokens/webserver/WebserverAPI.java b/src/main/java/io/supertokens/webserver/WebserverAPI.java index 28f3b62be..c3a45ce0b 100644 --- a/src/main/java/io/supertokens/webserver/WebserverAPI.java +++ b/src/main/java/io/supertokens/webserver/WebserverAPI.java @@ -461,16 +461,21 @@ protected String getRIDFromRequest(HttpServletRequest req) { return req.getHeader("rId"); } - protected SemVer getVersionFromRequest(HttpServletRequest req) { + protected SemVer getVersionFromRequest(HttpServletRequest req) throws ServletException { String version = req.getHeader("cdi-version"); if (version != null) { return new SemVer(version); } - String defaultCDIVersion = Config.getBaseConfig(main).getDefaultCDIVersion(); - if (defaultCDIVersion != null) { - return new SemVer(defaultCDIVersion); + try { + String defaultCDIVersion = Config.getConfig( + getAppIdentifierWithStorage(req).getAsPublicTenantIdentifier(), main).getDefaultCDIVersion(); + if (defaultCDIVersion != null) { + return new SemVer(defaultCDIVersion); + } + } catch (TenantOrAppNotFoundException e) { + throw new ServletException(e); } return getLatestCDIVersion(); diff --git a/src/test/java/io/supertokens/test/CDIVersionTest.java b/src/test/java/io/supertokens/test/CDIVersionTest.java index 2b3bc717d..f409d8f6e 100644 --- a/src/test/java/io/supertokens/test/CDIVersionTest.java +++ b/src/test/java/io/supertokens/test/CDIVersionTest.java @@ -19,11 +19,17 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.supertokens.ProcessState; +import io.supertokens.featureflag.EE_FEATURES; +import io.supertokens.featureflag.FeatureFlagTestContent; import io.supertokens.httpRequest.HttpRequest; +import io.supertokens.multitenancy.Multitenancy; +import io.supertokens.pluginInterface.multitenancy.*; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.test.httpRequest.HttpRequestForTesting; import io.supertokens.utils.SemVer; import io.supertokens.webserver.Webserver; import io.supertokens.webserver.WebserverAPI; +import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.AfterClass; @@ -33,6 +39,7 @@ import org.junit.rules.TestRule; import java.io.IOException; +import java.rmi.ServerException; import java.util.HashMap; import static junit.framework.TestCase.assertEquals; @@ -73,7 +80,8 @@ public String getPath() { } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { SemVer version = getVersionFromRequest(req); super.sendTextResponse(200, version.toString(), resp); @@ -110,7 +118,8 @@ public String getPath() { } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { SemVer version = getVersionFromRequest(req); super.sendTextResponse(200, version.toString(), resp); @@ -156,7 +165,8 @@ public String getPath() { } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { SemVer version = getVersionFromRequest(req); super.sendTextResponse(200, version.toString(), resp); @@ -197,7 +207,8 @@ public String getPath() { } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { SemVer version = getVersionFromRequest(req); super.sendTextResponse(200, version.toString(), resp); @@ -213,6 +224,78 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IO assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); } + @Test + public void testCDIVersionWorksPerApp() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false); + Utils.setValueInConfig("supertokens_default_cdi_version", "2.21"); + FeatureFlagTestContent.getInstance(process.getProcess()) + .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY}); + process.startProcess(); + + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + Webserver.getInstance(process.getProcess()).addAPI(new WebserverAPI(process.getProcess(), "") { + + private static final long serialVersionUID = 1L; + + @Override + public boolean checkAPIKey(HttpServletRequest req) { + return false; + } + + @Override + public String getPath() { + return "/version-test"; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + SemVer version = getVersionFromRequest(req); + + super.sendTextResponse(200, version.toString(), resp); + } + }); + + JsonObject config = new JsonObject(); + config.addProperty("supertokens_default_cdi_version", "3.0"); + Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig( + new TenantIdentifier(null, "a1", null), + new EmailPasswordConfig(true), + new ThirdPartyConfig(true, null), + new PasswordlessConfig(true), + config + ), 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); + + String response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/version-test", new HashMap<>(), 1000, 1000, null, + null, ""); + assertEquals("2.21", response); + + response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/appid-a1/version-test", new HashMap<>(), 1000, 1000, null, + null, ""); + assertEquals("3.0", response); + + response = HttpRequestForTesting.sendGETRequest(process.getProcess(), "", + "http://localhost:3567/appid-a1/t1/version-test", new HashMap<>(), 1000, 1000, null, + null, ""); + assertEquals("3.0", response); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + + } + @Test public void testJWKSEndpointWorksInAllCases() throws Exception { { diff --git a/src/test/java/io/supertokens/test/multitenant/ConfigTest.java b/src/test/java/io/supertokens/test/multitenant/ConfigTest.java index f95c80f01..17b874d73 100644 --- a/src/test/java/io/supertokens/test/multitenant/ConfigTest.java +++ b/src/test/java/io/supertokens/test/multitenant/ConfigTest.java @@ -1909,8 +1909,7 @@ public void testAllConflictingConfigs() throws Exception { "argon2_hashing_pool_size", "log_level", "firebase_password_hashing_pool_size", - "supertokens_saas_secret", - "supertokens_default_cdi_version" + "supertokens_saas_secret" }; Object[] disallowedValues = new Object[]{ 3567, @@ -1922,8 +1921,7 @@ public void testAllConflictingConfigs() throws Exception { 12, "DEBUG", 12, - "abcd1234abcd1234", - "3.0" + "abcd1234abcd1234" }; process.kill(); @@ -1981,6 +1979,7 @@ public void testAllConflictingConfigs() throws Exception { "argon2_parallelism", "bcrypt_log_rounds", "firebase_password_hashing_signer_key", + "supertokens_default_cdi_version", }; Object[][] conflictingValues = new Object[][]{ new Object[]{3600, 3601}, // access_token_validity @@ -1996,6 +1995,7 @@ public void testAllConflictingConfigs() throws Exception { new Object[]{2, 3}, // argon2_parallelism new Object[]{11, 12}, // bcrypt_log_rounds new Object[]{"abcd1234abcd1234abcd1234abcd1234", "qwer1234qwer1234qwer1234qwer1234"}, // firebase_password_hashing_signer_key + new Object[]{"2.21", "3.0"} // supertokens_default_cdi_version }; for (int i=0; i