Skip to content

Commit

Permalink
Merge branch '6.0' into license-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
sattvikc committed Sep 19, 2023
2 parents 35dd880 + 538ae12 commit de0236e
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ ALTER TABLE emailpassword_pswd_reset_tokens ADD CONSTRAINT emailpassword_pswd_re
ALTER TABLE emailpassword_pswd_reset_tokens ADD COLUMN email VARCHAR(256);
```

## [6.0.13] - 2023-09-15

- Fixes paid stats reporting for multitenancy

## [6.0.12] - 2023-09-04

- Fixes randomly occurring `serialization error for concurrent update` in `verifySession` API
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,13 @@ Mihály Lengyel</b></sub></a></td>
<tr>
<td align="center"><a href="https://github.com/sublimator"><img src="https://avatars.githubusercontent.com/u/525211?v=4" width="100px;" alt=""/><br /><sub><b>Nicholas Dudfield</b></sub></a></td>
<td align="center"><a href="https://github.com/Qdea"><img src="https://avatars.githubusercontent.com/u/58660439?v=4" width="100px;" alt=""/><br /><sub><b>Qdea</b></sub></a></td>
<td align="center"><a href="https://github.com/LukasKnuth"><img src="https://avatars.githubusercontent.com/u/692211?v=4" width="100px;" alt=""/><br /><sub><b>
Lukas Knuth</b></sub></a></td>
<td align="center"><a href="https://github.com/LukasKnuth"><img src="https://avatars.githubusercontent.com/u/692211?v=4" width="100px;" alt=""/><br /><sub><b>Lukas Knuth</b></sub></a></td>
<td align="center"><a href="https://github.com/melvynhills"><img src="https://avatars.githubusercontent.com/u/417315?v=4" width="100px;" alt=""/><br /><sub><b>
Melvyn Hills</b></sub></a></td>
<tr>
<td align="center"><a href="https://github.com/mattanimation"><img src="https://avatars.githubusercontent.com/u/1426997?v=4" width="100px;" alt=""/><br /><sub><b>Matt Murray</b></sub></a></td>
Melvyn Hills</b></sub></a></td>

</tr>
</table>

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 = "6.0.12"
version = "6.0.13"


repositories {
Expand Down
51 changes: 43 additions & 8 deletions ee/src/main/java/io/supertokens/ee/EEFeatureFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.TenantConfig;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.ThirdPartyConfig;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.session.sqlStorage.SessionSQLStorage;
Expand Down Expand Up @@ -58,6 +59,8 @@ public class EEFeatureFlag implements io.supertokens.featureflag.EEFeatureFlagIn
public static final String FEATURE_FLAG_KEY_IN_DB = "FEATURE_FLAG";
public static final String LICENSE_KEY_IN_DB = "LICENSE_KEY";

private static List<JsonObject> licenseCheckRequests = new ArrayList<>();

private static final String[] ENTERPRISE_THIRD_PARTY_IDS = new String[] {
"google-workspaces",
"okta",
Expand Down Expand Up @@ -150,6 +153,12 @@ public void syncFeatureFlagWithLicenseKey()
licenseKey = this.getLicenseKeyFromDb();
this.isLicenseKeyPresent = true;
} catch (NoLicenseKeyFoundException ex) {
try {
licenseKey = this.getRootLicenseKeyFromDb();
verifyLicenseKey(licenseKey); // also sends paid user stats for the app
} catch (NoLicenseKeyFoundException | InvalidLicenseKeyException ex2) {
// follow through below
}
this.isLicenseKeyPresent = false;
this.setEnabledEEFeaturesInDb(new EE_FEATURES[]{});
return;
Expand Down Expand Up @@ -440,6 +449,9 @@ private EE_FEATURES[] doServerCall(String licenseKey)
json.addProperty("licenseKey", licenseKey);
json.addProperty("superTokensVersion", Version.getVersion(main).getCoreVersion());
json.add("paidFeatureUsageStats", this.getPaidFeatureStats());
if (Main.isTesting) {
licenseCheckRequests.add(json);
}
ProcessState.getInstance(main).addState(ProcessState.PROCESS_STATE.LICENSE_KEY_CHECK_NETWORK_CALL, null);
JsonObject licenseCheckResponse = HttpRequest.sendJsonPOSTRequest(this.main, REQUEST_ID,
"https://api.supertokens.io/0/st/license/check",
Expand Down Expand Up @@ -522,17 +534,40 @@ private void removeLicenseKeyFromDb() throws StorageQueryException, TenantOrAppN
new KeyValueInfo(LICENSE_KEY_IN_DB_NOT_PRESENT_VALUE));
}

@Override
public String getLicenseKeyFromDb()
throws NoLicenseKeyFoundException, StorageQueryException, TenantOrAppNotFoundException {
Logging.debug(main, appIdentifier.getAsPublicTenantIdentifier(), "Attempting to fetch license key from db");
KeyValueInfo info = StorageLayer.getStorage(this.appIdentifier.getAsPublicTenantIdentifier(), main)
.getKeyValue(this.appIdentifier.getAsPublicTenantIdentifier(), LICENSE_KEY_IN_DB);
private String getLicenseKeyInDb(TenantIdentifier tenantIdentifier)
throws TenantOrAppNotFoundException, StorageQueryException, NoLicenseKeyFoundException {
Logging.debug(main, tenantIdentifier, "Attempting to fetch license key from db");
KeyValueInfo info = StorageLayer.getStorage(tenantIdentifier, main)
.getKeyValue(tenantIdentifier, LICENSE_KEY_IN_DB);
if (info == null || info.value.equals(LICENSE_KEY_IN_DB_NOT_PRESENT_VALUE)) {
Logging.debug(main, appIdentifier.getAsPublicTenantIdentifier(), "No license key found in db");
Logging.debug(main, tenantIdentifier, "No license key found in db");
throw new NoLicenseKeyFoundException();
}
Logging.debug(main, appIdentifier.getAsPublicTenantIdentifier(), "Fetched license key from db: " + info.value);
Logging.debug(main, tenantIdentifier, "Fetched license key from db: " + info.value);
return info.value;
}

@Override
public String getLicenseKeyFromDb()
throws NoLicenseKeyFoundException, StorageQueryException, TenantOrAppNotFoundException {
return getLicenseKeyInDb(appIdentifier.getAsPublicTenantIdentifier());
}

private String getRootLicenseKeyFromDb()
throws TenantOrAppNotFoundException, StorageQueryException, NoLicenseKeyFoundException {
return getLicenseKeyInDb(TenantIdentifier.BASE_TENANT);
}

@TestOnly
public static List<JsonObject> getLicenseCheckRequests() {
assert (Main.isTesting);
return licenseCheckRequests;
}

@TestOnly
public static void resetLisenseCheckRequests() {
licenseCheckRequests = new ArrayList<>();
}


}
129 changes: 129 additions & 0 deletions ee/src/test/java/io/supertokens/ee/test/TestMultitenancyStats.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package io.supertokens.ee.test;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.supertokens.ProcessState;
import io.supertokens.cronjobs.CronTaskTest;
import io.supertokens.ee.EEFeatureFlag;
import io.supertokens.ee.cronjobs.EELicenseCheck;
import io.supertokens.ee.test.httpRequest.HttpRequestForTesting;
import io.supertokens.featureflag.FeatureFlag;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.pluginInterface.multitenancy.*;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.webserver.WebserverAPI;
import org.junit.*;
import org.junit.rules.TestRule;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class TestMultitenancyStats {
@Rule
public TestRule watchman = Utils.getOnFailure();

@AfterClass
public static void afterTesting() {
Utils.afterTesting();
}

@Before
public void beforeEach() {
Utils.reset();
FeatureFlag.clearURLClassLoader();
}

private final String OPAQUE_KEY_WITH_MULTITENANCY_FEATURE = "ijaleljUd2kU9XXWLiqFYv5br8nutTxbyBqWypQdv2N-" +
"BocoNriPrnYQd0NXPm8rVkeEocN9ayq0B7c3Pv-BTBIhAZSclXMlgyfXtlwAOJk=9BfESEleW6LyTov47dXu";

@Test
public void testPaidStatsIsSentForAllAppsInMultitenancy() throws Exception {
String[] args = {"../../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
CronTaskTest.getInstance(process.main).setIntervalInSeconds(EELicenseCheck.RESOURCE_KEY, 1);
Assert.assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

if (StorageLayer.isInMemDb(process.main)) {
// cause we keep all features enabled in memdb anyway
return;
}

{
// Add the license
JsonObject requestBody = new JsonObject();

requestBody.addProperty("licenseKey", OPAQUE_KEY_WITH_MULTITENANCY_FEATURE);

HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "",
"http://localhost:3567/ee/license",
requestBody, 10000, 10000, null, WebserverAPI.getLatestCDIVersion().get(), "");
}

{
// Create tenants and apps
JsonObject config = new JsonObject();
StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess())
.modifyConfigToAddANewUserPoolForTesting(config, 1);

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier("127.0.0.1", null, null),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
config
), false);

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier("127.0.0.1", "a1", null),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
config
), false);

Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
new TenantIdentifier("127.0.0.1", "a1", "t1"),
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
config
), false);
}

Thread.sleep(2000); // Let all the cron tasks run

List<JsonObject> requests = EEFeatureFlag.getLicenseCheckRequests();
Set<TenantIdentifier> tenantIdentifiers = new HashSet<>();

for (JsonObject request : requests) {
if (request.has("paidFeatureUsageStats")) {
JsonObject paidStats = request.getAsJsonObject("paidFeatureUsageStats");
if (paidStats.has("multi_tenancy")) {
JsonObject mtStats = paidStats.getAsJsonObject("multi_tenancy");
String cud = mtStats.get("connectionUriDomain").getAsString();
String appId = mtStats.get("appId").getAsString();

JsonArray tenants = mtStats.get("tenants").getAsJsonArray();
for (JsonElement tenantElem : tenants) {
JsonObject tenant = tenantElem.getAsJsonObject();
String tenantId = tenant.get("tenantId").getAsString();

tenantIdentifiers.add(new TenantIdentifier(cud, appId, tenantId));
}
}
}
}

Assert.assertEquals(tenantIdentifiers.size(), 4);
Assert.assertTrue(tenantIdentifiers.contains(new TenantIdentifier(null, null, null)));
Assert.assertTrue(tenantIdentifiers.contains(new TenantIdentifier("127.0.0.1", null, null)));
Assert.assertTrue(tenantIdentifiers.contains(new TenantIdentifier("127.0.0.1", "a1", null)));
Assert.assertTrue(tenantIdentifiers.contains(new TenantIdentifier("127.0.0.1", "a1", "t1")));

process.kill();
Assert.assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}
}
2 changes: 2 additions & 0 deletions ee/src/test/java/io/supertokens/ee/test/Utils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.supertokens.ee.test;

import io.supertokens.Main;
import io.supertokens.ee.EEFeatureFlag;
import io.supertokens.pluginInterface.PluginInterfaceTesting;
import io.supertokens.storageLayer.StorageLayer;
import org.apache.tomcat.util.http.fileupload.FileUtils;
Expand Down Expand Up @@ -51,6 +52,7 @@ public static void reset() {
Main.isTesting = true;
PluginInterfaceTesting.isTesting = true;
Main.makeConsolePrintSilent = true;
EEFeatureFlag.resetLisenseCheckRequests();
String installDir = "../../";
try {

Expand Down
Binary file renamed jar/core-6.0.12.jar → jar/core-6.0.13.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ public void inputOutputTestStatic() throws Exception {
TokenInfo newToken = AccessToken.createNewAccessToken(process.getProcess(), "sessionHandle", "userId",
"refreshTokenHash1", "parentRefreshTokenHash1", jsonObj, "antiCsrfToken", expiryTime,
AccessToken.getLatestVersion(), true);
System.out.println(newToken.token);
AccessTokenInfo info = AccessToken.getInfoFromAccessToken(process.getProcess(), newToken.token, true);
assertEquals("sessionHandle", info.sessionHandle);
assertEquals("userId", info.recipeUserId);
Expand Down

0 comments on commit de0236e

Please sign in to comment.