Skip to content

Commit

Permalink
fix: merge with latest (#1028)
Browse files Browse the repository at this point in the history
* fix: pathrouter for tenant id stop words (#1022)

* fix: pathrouter for tenant id stop words

* fix: pathrouter for tenant id stop words

* fix: changelog

* adding dev-v9.1.2 tag to this commit to ensure building

* fix: 500 error to return actual message (#1023)

* adding dev-v9.1.2 tag to this commit to ensure building

* fix: tests (#1024)

* adding dev-v9.1.2 tag to this commit to ensure building

* fix: cicd tests (#1025)

* adding dev-v9.1.2 tag to this commit to ensure building

* Update release.md

* feat: add security feature (#1026)

* adding dev-v9.2.0 tag to this commit to ensure building

* fix: feature test fix (#1027)

* adding dev-v9.2.0 tag to this commit to ensure building

---------

Co-authored-by: Rishabh Poddar <[email protected]>
  • Loading branch information
sattvikc and rishabhpoddar authored Aug 20, 2024
1 parent aee7e88 commit 611e4e7
Show file tree
Hide file tree
Showing 17 changed files with 209 additions and 44 deletions.
8 changes: 4 additions & 4 deletions .github/ISSUE_TEMPLATE/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ labels:
- [ ] check if new env cnofigs need to be added
- [ ] try.supertokens.io
```
docker rm try-supertokens -f
docker rmi supertokens/supertokens-postgresql:<VERSION>
nano ~/try-supertokens/start_container.sh (update version tag)
~/try-supertokens/start_container.sh
sudo docker rm try-supertokens -f
sudo docker rmi supertokens/supertokens-postgresql:<VERSION>
nano ./start_container.sh (update version tag)
sudo ./start_container.sh
```
- [ ] Run tests against node sdk (all compatible versions)
- [ ] Run tests against python sdk (all compatible versions)
Expand Down
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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).

## Unreleased
## [Unreleased]

### Changes

Expand All @@ -24,9 +24,19 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Adds PATCH `/recipe/oauth/clients` for OAuth2 client update

### Migration

TODO: after plugin support

## [9.1.1] -2024-07-24
## [9.2.0] - 2024-08-20

- Adds `SECURITY` feature in `EE_FEATURES`.

## [9.1.2] - 2024-07-24

- Fixes path routing which rejected tenantId stop words even if it was not an exact stop word match. For example, `/hellotenant` is a valid tenantId prefix, however, it was being rejected for the stop word `hello`. - https://github.com/supertokens/supertokens-core/issues/1021
- 500 errors in core returns actual exception, since these APIs are developer facing, it makes easier to debug these errors.

## [9.1.1] - 2024-07-24

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
// }
//}

version = "9.1.1"
version = "9.2.0"


repositories {
Expand Down
Binary file modified cli/jar/cli.jar
Binary file not shown.
Binary file modified downloader/jar/downloader.jar
Binary file not shown.
Binary file modified ee/jar/ee.jar
Binary file not shown.
4 changes: 4 additions & 0 deletions ee/src/main/java/io/supertokens/ee/EEFeatureFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,10 @@ public JsonObject getPaidFeatureStats() throws StorageQueryException, TenantOrAp
if (feature == EE_FEATURES.ACCOUNT_LINKING) {
usageStats.add(EE_FEATURES.ACCOUNT_LINKING.toString(), getAccountLinkingStats());
}

if (feature == EE_FEATURES.SECURITY) {
usageStats.add(EE_FEATURES.SECURITY.toString(), new JsonObject());
}
}

usageStats.add("maus", getMAUs());
Expand Down
Binary file renamed jar/core-9.1.1.jar → jar/core-9.2.0.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion src/main/java/io/supertokens/featureflag/EE_FEATURES.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

public enum EE_FEATURES {
ACCOUNT_LINKING("account_linking"), MULTI_TENANCY("multi_tenancy"), TEST("test"),
DASHBOARD_LOGIN("dashboard_login"), MFA("mfa"), OAUTH("oauth");
DASHBOARD_LOGIN("dashboard_login"), MFA("mfa"), SECURITY("security"), OAUTH("oauth");

private final String name;

Expand Down
23 changes: 18 additions & 5 deletions src/main/java/io/supertokens/webserver/PathRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public PathRouter(Main main) {
}

public void addAPI(WebserverAPI newApi) {
this.apis.add(newApi);
this.apis.add(0, newApi); // add to the front so that the most recent API is checked first
for (WebserverAPI api : this.apis) {
for (WebserverAPI api2 : this.apis) {
if (api != api2 && api.getPath().equals(api2.getPath())) {
Expand Down Expand Up @@ -82,10 +82,23 @@ private WebserverAPI getAPIThatMatchesPath(HttpServletRequest req) {
apiPath = "/" + apiPath;
}

String tenantIdStopWords = String.join("|", Utils.INVALID_WORDS_FOR_TENANTID);
if (requestPath.matches(
"^(/appid-[a-z0-9-]*)?(/(?!" + tenantIdStopWords + ")[a-z0-9-]+)?" + apiPath + "/?$")) {
return api;
if (apiPath.endsWith("/")) {
apiPath = apiPath.substring(0, apiPath.length() - 1);
}

if (apiPath.isBlank()) {
String tenantIdStopWords = String.join("$|", Utils.INVALID_WORDS_FOR_TENANTID) + "$"; // Adds an end of string for each entry
tenantIdStopWords += "|" + String.join("/|", Utils.INVALID_WORDS_FOR_TENANTID) + "/"; // Adds a trailing slash for each entry
if (requestPath.matches(
"^(/appid-[a-z0-9-]*)?(/(?!" + tenantIdStopWords + ")[a-z0-9-]+)?" + "/?$")) {
return api;
}
} else {
String tenantIdStopWords = String.join("/|", Utils.INVALID_WORDS_FOR_TENANTID) + "/"; // Adds a trailing slash for each entry
if (requestPath.matches(
"^(/appid-[a-z0-9-]*)?(/(?!" + tenantIdStopWords + ")[a-z0-9-]+)?" + apiPath + "/?$")) {
return api;
}
}
}
for (WebserverAPI api : this.apis) {
Expand Down
36 changes: 24 additions & 12 deletions src/main/java/io/supertokens/webserver/WebserverAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,23 @@ private String getTenantId(HttpServletRequest req) {
if (!apiPath.startsWith("/")) {
apiPath = "/" + apiPath;
}
if (apiPath.equals("/")) {
if (path.equals("") || path.equals("/")) {
return null;
}
if (apiPath.endsWith("/")) {
apiPath = apiPath.substring(0, apiPath.length() - 1);
}

if (apiPath.isBlank() && (path.equals("") || path.equals("/"))) {
return null;
} else {
if (path.matches("^/appid-[a-z0-9-]*/[a-z0-9-]+" + apiPath + "/?$")) {
String tenantId = path.split("/")[2].toLowerCase();
if (tenantId.equals(TenantIdentifier.DEFAULT_TENANT_ID)) {
return null;
}

if (Utils.INVALID_WORDS_FOR_TENANTID.contains(tenantId)) {
return null;
}

return tenantId;
} else if (path.matches("^/appid-[a-z0-9-]*" + apiPath + "/?$")) {
return null;
Expand All @@ -281,12 +288,16 @@ private String getTenantId(HttpServletRequest req) {
if (tenantId.equals(TenantIdentifier.DEFAULT_TENANT_ID)) {
return null;
}

if (Utils.INVALID_WORDS_FOR_TENANTID.contains(tenantId)) {
return null;
}

return tenantId;
} else {
return null;
}
}
return null;
}

private String getAppId(HttpServletRequest req) {
Expand All @@ -295,10 +306,12 @@ private String getAppId(HttpServletRequest req) {
if (!apiPath.startsWith("/")) {
apiPath = "/" + apiPath;
}
if (apiPath.equals("/")) {
if (path.equals("") || path.equals("/")) {
return null;
}
if (apiPath.endsWith("/")) {
apiPath = apiPath.substring(0, apiPath.length() - 1);
}

if (apiPath.isBlank() && (path.equals("") || path.equals("/"))) {
return null;
} else {
if (path.matches("^/appid-[a-z0-9-]*(/[a-z0-9-]+)?" + apiPath + "/?$")) {
String appId = path.split("/")[1].toLowerCase();
Expand All @@ -310,7 +323,6 @@ private String getAppId(HttpServletRequest req) {
return null;
}
}
return null;
}

private String getConnectionUriDomain(HttpServletRequest req) throws ServletException {
Expand Down Expand Up @@ -528,10 +540,10 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws
} else if (rootCause instanceof BadPermissionException) {
sendTextResponse(403, rootCause.getMessage(), resp);
} else {
sendTextResponse(500, "Internal Error", resp);
sendTextResponse(500, rootCause.getMessage(), resp);
}
} else {
sendTextResponse(500, "Internal Error", resp);
sendTextResponse(500, e.getMessage(), resp);
}
}
Logging.info(main, tenantIdentifier, "API ended: " + req.getRequestURI() + ". Method: " + req.getMethod(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.utils.RateLimiter;
Expand Down Expand Up @@ -79,11 +80,29 @@ protected void handleRequest(HttpServletRequest req, HttpServletResponse resp) t
appIdentifier = getAppIdentifier(req);
storages = StorageLayer.getStoragesForApp(main, appIdentifier);
} catch (TenantOrAppNotFoundException e) {
// we send 500 status code
throw new ServletException(e);
}

if (req.getServletPath().equals("/")) {
String path = req.getServletPath();
TenantIdentifier tenantIdentifier = null;
try {
tenantIdentifier = getTenantIdentifier(req);
} catch (TenantOrAppNotFoundException e) {
super.sendTextResponse(404, "Not found", resp);
return;
}

if (path.startsWith("/appid-")) {
path = path.replace("/appid-"+tenantIdentifier.getAppId(), "");
}

if (!tenantIdentifier.getTenantId().equals(TenantIdentifier.DEFAULT_TENANT_ID)) {
path = path.replace("/" + tenantIdentifier.getTenantId(), "");
} else if (path.startsWith("/public") && tenantIdentifier.getTenantId().equals(TenantIdentifier.DEFAULT_TENANT_ID)) {
path = path.replace("/" + tenantIdentifier.getTenantId(), "");
}

if (path.equals("/") || path.isBlank()) {
// API is app specific
try {
RateLimiter rateLimiter = RateLimiter.getInstance(appIdentifier, super.main, 200);
Expand Down
6 changes: 5 additions & 1 deletion src/test/java/io/supertokens/test/FeatureFlagTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,9 @@ public void testNetworkCallIsMadeInCoreInit() throws Exception {
private final String OPAQUE_KEY_WTIH_MFA_FEATURE = "F1a=1VUxo7-tHNqFDwuhkkCPCB378A57uRU4=rVW01XBv63YizRb6ItTBu" +
"FHXQIvmceLTlOekCmHv7mwzEZJJKmO9N8pclQSbs4UBz8pzW5d107TIctJgBwy4upnBHUf";

private final String OPAQUE_KEY_WITH_SECURITY_FEATURE = "tje5MVjlRz0Kwzax-mKksdYpZvwNhQagFdHj=ma=W0H7WET9R0Hcpv" +
"Aui9r3wIk=swO2TIBLQNa94y10VQkzAa0Q0iw6GPzMeftJ4uvbnb1qpGpyf4K0cUwIZ76Pd9kZ";

@Test
public void testPaidStatsContainsAllEnabledFeatures() throws Exception {
String[] args = {"../"};
Expand All @@ -913,7 +916,8 @@ public void testPaidStatsContainsAllEnabledFeatures() throws Exception {
OPAQUE_KEY_WITH_MULTITENANCY_FEATURE,
OPAQUE_KEY_WITH_MFA_FEATURE,
OPAQUE_KEY_WITH_DASHBOARD_FEATURE,
OPAQUE_KEY_WITH_ACCOUNT_LINKING_FEATURE
OPAQUE_KEY_WITH_ACCOUNT_LINKING_FEATURE,
OPAQUE_KEY_WITH_SECURITY_FEATURE,
};

Set<EE_FEATURES> requiredFeatures = new HashSet<>();
Expand Down
54 changes: 42 additions & 12 deletions src/test/java/io/supertokens/test/HelloAPITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ public void testHelloAPIWithBasePath3() throws Exception {
"http://localhost:3567/hello/appid-hello/hello", // baseUrl + app + /hello
"http://localhost:3567/hello/appid-hello/hello/", // baseUrl + app + /hello
"http://localhost:3567/hello/appid-hello/test/hello", // baseUrl + app + tenant + /hello
"http://localhost:3567/hello/appid-hello", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/test", // baseUrl + app + tenant + /
"http://localhost:3567/hello/appid-hello/test/", // baseUrl + app + tenant + /
};

for (String helloUrl : HELLO_ROUTES) {
Expand All @@ -161,10 +165,7 @@ public void testHelloAPIWithBasePath3() throws Exception {
}

String[] NOT_FOUND_ROUTES = new String[]{
"http://localhost:3567/hello/appid-hello", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/test", // baseUrl + app + tenant + /
"http://localhost:3567/hello/appid-hello/test/", // baseUrl + app + tenant + /
"http://localhost:3567/hello/abcd",
};

// Not found
Expand Down Expand Up @@ -236,6 +237,11 @@ public void testWithBasePathThatHelloAPIDoesNotRequireAPIKeys() throws Exception
"http://localhost:3567/hello/appid-hello/hello", // baseUrl + app + /hello
"http://localhost:3567/hello/appid-hello/hello/", // baseUrl + app + /hello
"http://localhost:3567/hello/appid-hello/test/hello", // baseUrl + app + tenant + /hello

"http://localhost:3567/hello/appid-hello", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/test", // baseUrl + app + tenant + /
"http://localhost:3567/hello/appid-hello/test/", // baseUrl + app + tenant + /
};

for (String helloUrl : HELLO_ROUTES) {
Expand All @@ -250,10 +256,6 @@ public void testWithBasePathThatHelloAPIDoesNotRequireAPIKeys() throws Exception
String[] NOT_FOUND_ROUTES = new String[]{
"http://localhost:3567/abcd",
"http://localhost:3567",
"http://localhost:3567/hello/appid-hello", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/", // baseUrl + app + /
"http://localhost:3567/hello/appid-hello/test", // baseUrl + app + tenant + /
"http://localhost:3567/hello/appid-hello/test/", // baseUrl + app + tenant + /
};

// Not found
Expand Down Expand Up @@ -315,6 +317,23 @@ public void testThatHelloAPIDoesNotRequireAPIKeys() throws Exception {
new JsonObject()
), false);

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

String[] HELLO_ROUTES = new String[]{
"http://localhost:3567", // /
"http://localhost:3567/", // /
Expand All @@ -324,6 +343,20 @@ public void testThatHelloAPIDoesNotRequireAPIKeys() throws Exception {
"http://localhost:3567/appid-hello/hello", // app + /hello
"http://localhost:3567/appid-hello/hello/", // app + /hello
"http://localhost:3567/appid-hello/test/hello", // app + tenant + /hello

"http://localhost:3567/hellotenant",
"http://localhost:3567/hellotenant/",
"http://localhost:3567/hellotenant/hello",

"http://localhost:3567/appid-hello", // app + /
"http://localhost:3567/appid-hello/", // app + /
"http://localhost:3567/appid-hello/public",
"http://localhost:3567/appid-hello/public/",
"http://localhost:3567/appid-hello/test", // app + tenant + /
"http://localhost:3567/appid-hello/test/", // app + tenant + /
"http://localhost:3567/appid-hello/hellotenant",
"http://localhost:3567/appid-hello/hellotenant/",
"http://localhost:3567/appid-hello/hellotenant/hello",
};

for (String helloUrl : HELLO_ROUTES) {
Expand All @@ -337,15 +370,12 @@ public void testThatHelloAPIDoesNotRequireAPIKeys() throws Exception {

String[] NOT_FOUND_ROUTES = new String[]{
"http://localhost:3567/abcd",
"http://localhost:3567/appid-hello", // app + /
"http://localhost:3567/appid-hello/", // app + /
"http://localhost:3567/appid-hello/test", // app + tenant + /
"http://localhost:3567/appid-hello/test/", // app + tenant + /
};

// Not found
for (String notFoundUrl : NOT_FOUND_ROUTES) {
try {
System.out.println(notFoundUrl);
String res = HttpRequestForTesting.sendGETRequest(process.getProcess(), "",
notFoundUrl, null, 1000, 1000,
null, Utils.getCdiVersionStringLatestForTests(), "");
Expand Down
Loading

0 comments on commit 611e4e7

Please sign in to comment.