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

Mfa multitenancy #841

Merged
merged 19 commits into from
Oct 27, 2023
3 changes: 2 additions & 1 deletion coreDriverInterfaceSupported.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"2.20",
"2.21",
"3.0",
"4.0"
"4.0",
"4.1"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public void testPaidStatsIsSentForAllAppsInMultitenancy() throws Exception {
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new TotpConfig(false), null, null,
config
), false);

Expand All @@ -86,6 +87,7 @@ public void testPaidStatsIsSentForAllAppsInMultitenancy() throws Exception {
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new TotpConfig(false), null, null,
config
), false);

Expand All @@ -94,6 +96,7 @@ public void testPaidStatsIsSentForAllAppsInMultitenancy() throws Exception {
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new TotpConfig(false), null, null,
config
), false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ public TenantConfig map(ResultSet result) throws StorageQueryException {
new EmailPasswordConfig(result.getBoolean("email_password_enabled")),
new ThirdPartyConfig(result.getBoolean("third_party_enabled"), this.providers),
new PasswordlessConfig(result.getBoolean("passwordless_enabled")),
JsonUtils.stringToJsonObject(result.getString("core_config"))
new TotpConfig(false), // TODO
null, null, JsonUtils.stringToJsonObject(result.getString("core_config"))
// TODO
);
} catch (Exception e) {
throw new StorageQueryException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ public static void init(Main main) throws StorageQueryException, IOException {
new TenantConfig(
new TenantIdentifier(null, null, null),
new EmailPasswordConfig(true), new ThirdPartyConfig(true, null),
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
new PasswordlessConfig(true), new JsonObject()), false, false, false);
new PasswordlessConfig(true), new TotpConfig(true),
null, null, new JsonObject()), false, false, false);
// Not force reloading all resources here (the last boolean in the function above)
// because the ucl for the FeatureFlag is not yet loaded and results in an empty
// instance of eeFeatureFlag. This is applicable only when the core is starting on
Expand All @@ -95,7 +96,8 @@ private TenantConfig[] getAllTenantsFromDb() throws StorageQueryException {
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
new TotpConfig(true),
null, null, new JsonObject()
)
};
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/supertokens/utils/SemVer.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class SemVer implements Comparable<SemVer> {
public static final SemVer v2_21 = new SemVer("2.21");
public static final SemVer v3_0 = new SemVer("3.0");
public static final SemVer v4_0 = new SemVer("4.0");
public static final SemVer v4_1 = new SemVer("4.1");

final private String version;

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/supertokens/webserver/WebserverAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ public abstract class WebserverAPI extends HttpServlet {
supportedVersions.add(SemVer.v2_21);
supportedVersions.add(SemVer.v3_0);
supportedVersions.add(SemVer.v4_0);
supportedVersions.add(SemVer.v4_1);
}

public static SemVer getLatestCDIVersion() {
return SemVer.v4_0;
return SemVer.v4_1;
}

public SemVer getLatestCDIVersionForRequest(HttpServletRequest req)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ public BaseCreateOrUpdate(Main main) {

protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdentifier,
TenantIdentifier targetTenantIdentifier, Boolean emailPasswordEnabled,
Boolean thirdPartyEnabled, Boolean passwordlessEnabled, JsonObject coreConfig,
HttpServletResponse resp)
Boolean thirdPartyEnabled, Boolean passwordlessEnabled, Boolean totpEnabled,
boolean hasFirstFactors, String[] firstFactors,
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
boolean hasDefaultRequiredFactorIds, String[] defaultRequiredFactorIds,
JsonObject coreConfig, HttpServletResponse resp)
throws ServletException, IOException {

TenantConfig tenantConfig = Multitenancy.getTenantInfo(main,
Expand All @@ -63,7 +65,8 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
new JsonObject()
new TotpConfig(true),
null, null, new JsonObject()
);
} else {
// We disable all recipes by default while creating tenant
Expand All @@ -72,7 +75,8 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent
new EmailPasswordConfig(false),
new ThirdPartyConfig(false, null),
new PasswordlessConfig(false),
new JsonObject()
new TotpConfig(false),
null, null, new JsonObject()
);
}
createdNew = true;
Expand All @@ -84,7 +88,8 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent
new EmailPasswordConfig(emailPasswordEnabled),
tenantConfig.thirdPartyConfig,
tenantConfig.passwordlessConfig,
tenantConfig.coreConfig
tenantConfig.totpConfig,
tenantConfig.firstFactors, tenantConfig.defaultRequiredFactorIds, tenantConfig.coreConfig
);
}

Expand All @@ -94,7 +99,8 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent
tenantConfig.emailPasswordConfig,
new ThirdPartyConfig(thirdPartyEnabled, tenantConfig.thirdPartyConfig.providers),
tenantConfig.passwordlessConfig,
tenantConfig.coreConfig
tenantConfig.totpConfig,
tenantConfig.firstFactors, tenantConfig.defaultRequiredFactorIds, tenantConfig.coreConfig
);
}

Expand All @@ -104,7 +110,41 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent
tenantConfig.emailPasswordConfig,
tenantConfig.thirdPartyConfig,
new PasswordlessConfig(passwordlessEnabled),
tenantConfig.coreConfig
tenantConfig.totpConfig,
tenantConfig.firstFactors, tenantConfig.defaultRequiredFactorIds, tenantConfig.coreConfig
);
}

if (totpEnabled != null) {
tenantConfig = new TenantConfig(
tenantConfig.tenantIdentifier,
tenantConfig.emailPasswordConfig,
tenantConfig.thirdPartyConfig,
tenantConfig.passwordlessConfig,
new TotpConfig(totpEnabled),
tenantConfig.firstFactors, tenantConfig.defaultRequiredFactorIds, tenantConfig.coreConfig
);
}

if (hasFirstFactors) {
tenantConfig = new TenantConfig(
tenantConfig.tenantIdentifier,
tenantConfig.emailPasswordConfig,
tenantConfig.thirdPartyConfig,
tenantConfig.passwordlessConfig,
tenantConfig.totpConfig,
firstFactors, tenantConfig.defaultRequiredFactorIds, tenantConfig.coreConfig
);
}

if (hasDefaultRequiredFactorIds) {
tenantConfig = new TenantConfig(
tenantConfig.tenantIdentifier,
tenantConfig.emailPasswordConfig,
tenantConfig.thirdPartyConfig,
tenantConfig.passwordlessConfig,
tenantConfig.totpConfig,
tenantConfig.firstFactors, defaultRequiredFactorIds, tenantConfig.coreConfig
);
}

Expand All @@ -115,7 +155,8 @@ protected void handle(HttpServletRequest req, TenantIdentifier sourceTenantIdent
tenantConfig.emailPasswordConfig,
tenantConfig.thirdPartyConfig,
tenantConfig.passwordlessConfig,
coreConfig
tenantConfig.totpConfig,
tenantConfig.firstFactors, tenantConfig.defaultRequiredFactorIds, coreConfig
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@

package io.supertokens.webserver.api.multitenancy;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.multitenancy.exception.BadPermissionException;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.utils.SemVer;
import io.supertokens.webserver.InputParser;
import io.supertokens.webserver.Utils;
import io.supertokens.webserver.api.multitenancy.BaseCreateOrUpdate;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class CreateOrUpdateAppAPI extends BaseCreateOrUpdate {

Expand Down Expand Up @@ -56,6 +59,38 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
Boolean passwordlessEnabled = InputParser.parseBooleanOrThrowError(input, "passwordlessEnabled", true);
JsonObject coreConfig = InputParser.parseJsonObjectOrThrowError(input, "coreConfig", true);

Boolean totpEnabled = null;
String[] firstFactors = null;
boolean hasFirstFactors = false;
String[] defaultRequiredFactorIds = null;
boolean hasDefaultRequiredFactorIds = false;

if (getVersionFromRequest(req).greaterThanOrEqualTo(SemVer.v4_1)) {
totpEnabled = InputParser.parseBooleanOrThrowError(input, "totpEnabled", true);
hasFirstFactors = input.has("firstFactors");
if (hasFirstFactors && !input.get("firstFactors").isJsonNull()) {
JsonArray firstFactorsArr = InputParser.parseArrayOrThrowError(input, "firstFactors", true);
firstFactors = new String[firstFactorsArr.size()];
for (int i = 0; i < firstFactors.length; i++) {
firstFactors[i] = InputParser.parseStringFromElementOrThrowError(firstFactorsArr.get(i), "firstFactors", false);
}
if (firstFactors.length != new HashSet<>(Arrays.asList(firstFactors)).size()) {
throw new ServletException(new BadRequestException("firstFactors input should not contain duplicate values"));
}
}
hasDefaultRequiredFactorIds = input.has("defaultRequiredFactorIds");
if (hasDefaultRequiredFactorIds && !input.get("defaultRequiredFactorIds").isJsonNull()) {
JsonArray defaultRequiredFactorIdsArr = InputParser.parseArrayOrThrowError(input, "defaultRequiredFactorIds", true);
defaultRequiredFactorIds = new String[defaultRequiredFactorIdsArr.size()];
for (int i = 0; i < defaultRequiredFactorIds.length; i++) {
defaultRequiredFactorIds[i] = InputParser.parseStringFromElementOrThrowError(defaultRequiredFactorIdsArr.get(i), "defaultRequiredFactorIds", false);
}
if (defaultRequiredFactorIds.length != new HashSet<>(Arrays.asList(defaultRequiredFactorIds)).size()) {
throw new ServletException(new BadRequestException("defaultRequiredFactorIds input should not contain duplicate values"));
}
}
}

TenantIdentifier sourceTenantIdentifier;
try {
sourceTenantIdentifier = this.getTenantIdentifierWithStorageFromRequest(req);
Expand All @@ -66,7 +101,9 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
super.handle(
req, sourceTenantIdentifier,
new TenantIdentifier(sourceTenantIdentifier.getConnectionUriDomain(), appId, null),
emailPasswordEnabled, thirdPartyEnabled, passwordlessEnabled, coreConfig, resp);
emailPasswordEnabled, thirdPartyEnabled, passwordlessEnabled,
totpEnabled, hasFirstFactors, firstFactors, hasDefaultRequiredFactorIds, defaultRequiredFactorIds,
coreConfig, resp);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,22 @@

package io.supertokens.webserver.api.multitenancy;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.supertokens.Main;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.utils.SemVer;
import io.supertokens.webserver.InputParser;
import io.supertokens.webserver.Utils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class CreateOrUpdateConnectionUriDomainAPI extends BaseCreateOrUpdate {

Expand Down Expand Up @@ -54,6 +59,38 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
Boolean passwordlessEnabled = InputParser.parseBooleanOrThrowError(input, "passwordlessEnabled", true);
JsonObject coreConfig = InputParser.parseJsonObjectOrThrowError(input, "coreConfig", true);

Boolean totpEnabled = null;
String[] firstFactors = null;
boolean hasFirstFactors = false;
String[] defaultRequiredFactorIds = null;
boolean hasDefaultRequiredFactorIds = false;

if (getVersionFromRequest(req).greaterThanOrEqualTo(SemVer.v4_1)) {
totpEnabled = InputParser.parseBooleanOrThrowError(input, "totpEnabled", true);
hasFirstFactors = input.has("firstFactors");
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
if (hasFirstFactors && !input.get("firstFactors").isJsonNull()) {
JsonArray firstFactorsArr = InputParser.parseArrayOrThrowError(input, "firstFactors", true);
firstFactors = new String[firstFactorsArr.size()];
for (int i = 0; i < firstFactors.length; i++) {
firstFactors[i] = InputParser.parseStringFromElementOrThrowError(firstFactorsArr.get(i), "firstFactors", false);
}
if (firstFactors.length != new HashSet<>(Arrays.asList(firstFactors)).size()) {
throw new ServletException(new BadRequestException("firstFactors input should not contain duplicate values"));
}
}
hasDefaultRequiredFactorIds = input.has("defaultRequiredFactorIds");
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
if (hasDefaultRequiredFactorIds && !input.get("defaultRequiredFactorIds").isJsonNull()) {
JsonArray defaultRequiredFactorIdsArr = InputParser.parseArrayOrThrowError(input, "defaultRequiredFactorIds", true);
defaultRequiredFactorIds = new String[defaultRequiredFactorIdsArr.size()];
for (int i = 0; i < defaultRequiredFactorIds.length; i++) {
defaultRequiredFactorIds[i] = InputParser.parseStringFromElementOrThrowError(defaultRequiredFactorIdsArr.get(i), "defaultRequiredFactorIds", false);
}
if (defaultRequiredFactorIds.length != new HashSet<>(Arrays.asList(defaultRequiredFactorIds)).size()) {
throw new ServletException(new BadRequestException("defaultRequiredFactorIds input should not contain duplicate values"));
}
}
}

TenantIdentifier sourceTenantIdentifier;
try {
sourceTenantIdentifier = this.getTenantIdentifierWithStorageFromRequest(req);
Expand All @@ -64,7 +101,9 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IO
super.handle(
req, sourceTenantIdentifier,
new TenantIdentifier(connectionUriDomain, null, null),
emailPasswordEnabled, thirdPartyEnabled, passwordlessEnabled, coreConfig, resp);
emailPasswordEnabled, thirdPartyEnabled, passwordlessEnabled,
totpEnabled, hasFirstFactors, firstFactors, hasDefaultRequiredFactorIds, defaultRequiredFactorIds,
coreConfig, resp);

}
}
Loading
Loading