Skip to content

Commit

Permalink
fix: pathrouter for tenant id stop words
Browse files Browse the repository at this point in the history
  • Loading branch information
sattvikc committed Aug 8, 2024
1 parent d7412e4 commit c4355d9
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 22 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

## [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`.

## [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.1.2"


repositories {
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
32 changes: 22 additions & 10 deletions src/main/java/io/supertokens/webserver/WebserverAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -259,16 +259,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 @@ -277,12 +284,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 @@ -291,10 +302,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 @@ -306,7 +319,6 @@ private String getAppId(HttpServletRequest req) {
return null;
}
}
return null;
}

private String getConnectionUriDomain(HttpServletRequest req) throws ServletException {
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,27 @@ 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(), "");
}

if (path.equals("/") || path.isBlank()) {
// API is app specific
try {
RateLimiter rateLimiter = RateLimiter.getInstance(appIdentifier, super.main, 200);
Expand Down
34 changes: 30 additions & 4 deletions src/test/java/io/supertokens/test/HelloAPITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,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 +341,18 @@ 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/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 +366,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

0 comments on commit c4355d9

Please sign in to comment.