Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds base_path config #363

Merged
merged 1 commit into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

- Supporting CDI v2.12
- Adding the `userId` to the reponse of `recipe/user/password/reset`
- Adds support for providing base path for all APIs: https://github.com/supertokens/supertokens-node/issues/252

### New config param:

- `base_path` - default is `""` (No base path)

## [3.8.0] - 2022-01-14

Expand All @@ -21,9 +26,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Database changes

- Adds new tables for passwordless:
- `passwordless_users` that stores the users of the passwordless recipe
- `passwordless_devices` that stores devices/information about passwordless login attempts
- `passwordless_codes` that stores the codes each device can consume to finish the login process
- `passwordless_users` that stores the users of the passwordless recipe
- `passwordless_devices` that stores devices/information about passwordless login attempts
- `passwordless_codes` that stores the codes each device can consume to finish the login process

### Changes

Expand All @@ -40,7 +45,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixes

- Issue with JWT expiry always being lower than expected
- Modulus and exponent for JsonWebKeys are now sent as unsigned when fetching public keys from the /jwt/jwks.json
- Modulus and exponent for JsonWebKeys are now sent as unsigned when fetching public keys from the /jwt/jwks.json
endpoint. Both values are url encoded without any padding.

### Changes
Expand Down
4 changes: 4 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ core_config_version: 0
# (OPTIONAL | Default: false). Learn more about Telemetry here:
# https://github.com/supertokens/supertokens-core/wiki/Telemetry
# disable_telemetry:


# (OPTIONAL | Default: ""). Used to prepend a base path to all APIs when querying the core.
# base_path:
5 changes: 4 additions & 1 deletion devConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,7 @@ core_config_version: 0
# Important: This is set to true here but is uncommented in config.yaml. The reason is that when testing with drivers or
# in CICD, we do not want to send telemetry data. For unit tests, this is commented again in Utils.reset function (in
# the test package)
disable_telemetry: true
disable_telemetry: true

# (OPTIONAL | Default: ""). Used to prepend a base path to all APIs when querying the core.
# base_path:
22 changes: 22 additions & 0 deletions src/main/java/io/supertokens/config/CoreConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ public class CoreConfig {
@JsonProperty
private boolean webserver_https_enabled = false;

@JsonProperty
private String base_path = "";

public String getBasePath() {
if (base_path == null || base_path.equals("/")) {
return "";
}
return base_path;
}

public int getConfigVersion() {
return core_config_version;
}
Expand Down Expand Up @@ -263,6 +273,18 @@ void validateAndInitialise(Main main) throws IOException {
}
}

if (base_path != null && !base_path.equals("") && !base_path.equals("/")) {
if (base_path.contains(" ")) {
throw new QuitProgramException("Invalid characters in base_path config");
}
if (!base_path.startsWith("/")) {
throw new QuitProgramException("base_path must start with a '/'");
}
if (base_path.endsWith("/")) {
throw new QuitProgramException("base_path cannot end with '/'");
}
}

if (!getInfoLogPath(main).equals("null")) {
File infoLog = new File(getInfoLogPath(main));
if (!infoLog.exists()) {
Expand Down
11 changes: 5 additions & 6 deletions src/main/java/io/supertokens/webserver/Webserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,15 @@
import io.supertokens.webserver.api.core.UsersAPI;
import io.supertokens.webserver.api.core.UsersCountAPI;
import io.supertokens.webserver.api.core.*;
import io.supertokens.webserver.api.emailpassword.UserAPI;
import io.supertokens.webserver.api.emailpassword.*;
import io.supertokens.webserver.api.emailverification.GenerateEmailVerificationTokenAPI;
import io.supertokens.webserver.api.emailverification.RevokeAllTokensForUserAPI;
import io.supertokens.webserver.api.emailverification.UnverifyEmailAPI;
import io.supertokens.webserver.api.emailverification.VerifyEmailAPI;
import io.supertokens.webserver.api.jwt.JWKSAPI;
import io.supertokens.webserver.api.jwt.JWTSigningAPI;
import io.supertokens.webserver.api.passwordless.GetCodesAPI;
import io.supertokens.webserver.api.passwordless.ConsumeCodeAPI;
import io.supertokens.webserver.api.passwordless.DeleteCodesAPI;
import io.supertokens.webserver.api.passwordless.DeleteCodeAPI;
import io.supertokens.webserver.api.passwordless.CreateCodeAPI;
import io.supertokens.webserver.api.passwordless.*;
import io.supertokens.webserver.api.session.*;
import io.supertokens.webserver.api.thirdparty.GetUsersByEmailAPI;
import io.supertokens.webserver.api.thirdparty.SignInUpAPI;
Expand All @@ -64,7 +61,7 @@ public class Webserver extends ResourceDistributor.SingletonResource {
? "webserver-temp\\" + UUID.randomUUID().toString() + "\\"
: "webserver-temp/" + UUID.randomUUID().toString() + "/";
// contextPath is the prefix to all paths for all URLs. So it's "" for us.
private final String CONTEXT_PATH = "";
private String CONTEXT_PATH = "";
private final Main main;

private final WebServerLogging logging;
Expand Down Expand Up @@ -93,6 +90,8 @@ public void start() {
webserverTemp.mkdir();
}

CONTEXT_PATH = Config.getConfig(main).getBasePath();

// this will make it so that if there is a failure, then tomcat will throw an
// error...
System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
Expand Down
135 changes: 135 additions & 0 deletions src/test/java/io/supertokens/test/WebserverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,141 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IO
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STOPPED));
}

@Test
public void invalidBasePathTest() throws InterruptedException, IOException {
{
Utils.setValueInConfig("base_path", "somepath/");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
EventAndException e = process.checkOrWaitForEvent(PROCESS_STATE.INIT_FAILURE);
assertTrue(e != null && e.exception instanceof QuitProgramException
&& e.exception.getMessage().equals("base_path must start with a '/'"));
Utils.reset();
}

{
Utils.setValueInConfig("base_path", "/somepath/");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
EventAndException e = process.checkOrWaitForEvent(PROCESS_STATE.INIT_FAILURE);
assertTrue(e != null && e.exception instanceof QuitProgramException
&& e.exception.getMessage().equals("base_path cannot end with '/'"));
Utils.reset();
}

{
Utils.setValueInConfig("base_path", "/some path");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
EventAndException e = process.checkOrWaitForEvent(PROCESS_STATE.INIT_FAILURE);
assertTrue(e != null && e.exception instanceof QuitProgramException
&& e.exception.getMessage().equals("Invalid characters in base_path config"));
Utils.reset();
}

}

@Test
public void validBasePath() throws InterruptedException, IOException, HttpResponseException {
{
Utils.setValueInConfig("base_path", "/");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STARTED));

String response = HttpRequest.sendGETRequest(process.getProcess(), "", "http://localhost:3567/hello", null,
1000, 1000, null);
assertEquals("Hello", response);

process.kill();
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STOPPED));
Utils.reset();
}

{
Utils.setValueInConfig("base_path", "\"\"");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STARTED));

String response = HttpRequest.sendGETRequest(process.getProcess(), "", "http://localhost:3567/hello", null,
1000, 1000, null);
assertEquals("Hello", response);

process.kill();
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STOPPED));
Utils.reset();
}

{
Utils.setValueInConfig("base_path", "\"/test\"");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STARTED));

String response = HttpRequest.sendGETRequest(process.getProcess(), "", "http://localhost:3567/test/hello",
null, 1000, 1000, null);
assertEquals("Hello", response);

process.kill();
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STOPPED));
Utils.reset();
}

{
Utils.setValueInConfig("base_path", "\"/test/path\"");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STARTED));

String response = HttpRequest.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/test/path/hello", null, 1000, 1000, null);
assertEquals("Hello", response);

process.kill();
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STOPPED));
}

{
Utils.setValueInConfig("base_path", "\"/te3st/Pa23th\"");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STARTED));

{
String response = HttpRequest.sendGETRequest(process.getProcess(), "",
"http://localhost:3567/te3st/Pa23th/hello", null, 1000, 1000, null);
assertEquals("Hello", response);
}

try {
HttpRequest.sendGETRequest(process.getProcess(), "", "http://localhost:3567/hello", null, 1000, 1000,
null);
fail();
} catch (Exception e) {
assert (e.getMessage().startsWith("Http error. Status Code: 404"));
}

process.kill();
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STOPPED));
}

{
Utils.setValueInConfig("base_path", "");
String[] args = { "../" };
TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STARTED));

String response = HttpRequest.sendGETRequest(process.getProcess(), "", "http://localhost:3567/hello", null,
1000, 1000, null);
assertEquals("Hello", response);

process.kill();
assertNotNull(process.checkOrWaitForEvent(PROCESS_STATE.STOPPED));
}

}

private static RecipeRouter getRecipeRouter(TestingProcess process, String recipe_1, String recipe_2)
throws Exception {
WebserverAPI recipe_1_api = new WebserverAPI(process.getProcess(), recipe_1) {
Expand Down