From 5530d008b6b3f9c6002eff1af029ad5c1ecd8f51 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 16 Jan 2023 14:31:26 +0530 Subject: [PATCH 01/93] adds interface for multi tenancy --- .../multitenancy/EmailPasswordConfig.java | 25 ++++++ .../multitenancy/MultitenancyStorage.java | 37 +++++++++ .../multitenancy/PasswordlessConfig.java | 25 ++++++ .../multitenancy/TenantConfig.java | 40 ++++++++++ .../multitenancy/ThirdPartyConfig.java | 77 +++++++++++++++++++ .../exceptions/DuplicateTenantException.java | 23 ++++++ .../exceptions/UnknownTenantException.java | 23 ++++++ 7 files changed, 250 insertions(+) create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/UnknownTenantException.java diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java new file mode 100644 index 00000000..fc00bb86 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +public class EmailPasswordConfig { + public boolean enabled; + + public EmailPasswordConfig(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java new file mode 100644 index 00000000..7a24e04a --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; +import io.supertokens.pluginInterface.multitenancy.exceptions.UnknownTenantException; + +public interface MultitenancyStorage extends Storage { + + void createTenant(TenantConfig config) throws DuplicateTenantException; + + void overwriteTenantConfig(TenantConfig config) throws UnknownTenantException; + + void deleteTenant(String tenantId) throws UnknownTenantException; + + TenantConfig getTenantConfigForTenantId(String tenantId); + + TenantConfig[] getAllTenants(); + + TenantConfig[] getAllTenantsWithThirdPartyId(String thirdPartyId); + +} \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java new file mode 100644 index 00000000..dc629643 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +public class PasswordlessConfig { + public boolean enabled; + + public PasswordlessConfig(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java new file mode 100644 index 00000000..d1960e28 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +import com.google.gson.JsonObject; + +public class TenantConfig { + public String connectionUriDomain; + public String tenantId; + public EmailPasswordConfig emailPasswordConfig; + public PasswordlessConfig passwordlessConfig; + public ThirdPartyConfig thirdPartyConfig; + + public JsonObject coreConfig; + + public TenantConfig(String connectionUriDomain, String tenantId, EmailPasswordConfig emailPasswordConfig, + ThirdPartyConfig thirdPartyConfig, + PasswordlessConfig passwordlessConfig, JsonObject coreConfig) { + this.connectionUriDomain = connectionUriDomain; + this.tenantId = tenantId; + this.coreConfig = coreConfig; + this.emailPasswordConfig = emailPasswordConfig; + this.passwordlessConfig = passwordlessConfig; + this.thirdPartyConfig = thirdPartyConfig; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java new file mode 100644 index 00000000..ac16e718 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +import com.google.gson.JsonObject; + +public class ThirdPartyConfig { + public boolean enabled; + public Provider[] providers; + + public ThirdPartyConfig(boolean enabled, Provider[] providers) { + this.enabled = enabled; + this.providers = providers; + } + + public class Provider { + public String thirdPartyId; + public String name; + public ProviderClients[] clients; + public String authorizationEndpoint; + public JsonObject authorizationEndpointQueryParams; + public String tokenEndpoint; + public JsonObject tokenEndpointBodyParams; + public String userInfoEndpoint; + public JsonObject userInfoEndpointQueryParams; + public JsonObject userInfoEndpointHeaders; + public String jwksURI; + public String oidcDiscoveryEndpoint; + public boolean requireEmail; + public UserInfoMap userInfoMap; + } + + public class ProviderClients { + public String clientType; + public String clientId; + public String clientSecret; + public String[] scope; + public boolean forcePKCE; + public JsonObject additionalConfig; + } + + public class UserInfoMap { + public UserInfoMapKeyValue fromIdTokenPayload; + public UserInfoMapKeyValue fromUserInfoAPI; + + public UserInfoMap(UserInfoMapKeyValue fromIdTokenPayload, UserInfoMapKeyValue fromUserInfoAPI) { + this.fromIdTokenPayload = fromIdTokenPayload; + this.fromUserInfoAPI = fromUserInfoAPI; + } + } + + public class UserInfoMapKeyValue { + public String userId; + public String email; + public String emailVerified; + + public UserInfoMapKeyValue(String userId, String email, String emailVerified) { + this.userId = userId; + this.email = email; + this.emailVerified = emailVerified; + } + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java new file mode 100644 index 00000000..86f04851 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy.exceptions; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class DuplicateTenantException extends EmailPasswordException { + +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/UnknownTenantException.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/UnknownTenantException.java new file mode 100644 index 00000000..49e1f23c --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/UnknownTenantException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy.exceptions; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class UnknownTenantException extends EmailPasswordException { + private static final long serialVersionUID = 7345237610253685511L; +} From 094f02ddad922dfde51d4c5522a69a11e63e44e5 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 19 Jan 2023 14:01:12 +0530 Subject: [PATCH 02/93] changes storage layer to take json instead of config file path --- .../supertokens/pluginInterface/Storage.java | 6 ++-- .../exceptions/InvalidConfigException.java | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/exceptions/InvalidConfigException.java diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 2eb896a4..cfee8ca3 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -17,6 +17,8 @@ package io.supertokens.pluginInterface; +import com.google.gson.JsonObject; +import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import java.util.Set; @@ -26,7 +28,7 @@ public interface Storage { // if silent is true, do not log anything out on the console void constructor(String processId, boolean silent); - void loadConfig(String configFilePath, Set logLevels); + void loadConfig(JsonObject jsonConfig, Set logLevels) throws InvalidConfigException; void initFileLogging(String infoLogPath, String errorLogPath); @@ -49,7 +51,7 @@ public interface Storage { void setStorageLayerEnabled(boolean enabled); - boolean canBeUsed(String configFilePath); + boolean canBeUsed(JsonObject configJson); // this function will be used in the createUserIdMapping and deleteUserIdMapping functions to check if the userId is // being used in NonAuth recipes. diff --git a/src/main/java/io/supertokens/pluginInterface/exceptions/InvalidConfigException.java b/src/main/java/io/supertokens/pluginInterface/exceptions/InvalidConfigException.java new file mode 100644 index 00000000..3dfdcd45 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/exceptions/InvalidConfigException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.exceptions; + +public class InvalidConfigException extends Exception { + public InvalidConfigException(String message) { + super(message); + } + + public InvalidConfigException() { + super(); + } + + public InvalidConfigException(Exception e) { + super(e); + } +} From ef6a9ccd1e9a5df643039eaff947c49b0b37917c Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 19 Jan 2023 15:47:14 +0530 Subject: [PATCH 03/93] adds new interface to indentify a user pool --- src/main/java/io/supertokens/pluginInterface/Storage.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index cfee8ca3..734a89f4 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -30,6 +30,14 @@ public interface Storage { void loadConfig(JsonObject jsonConfig, Set logLevels) throws InvalidConfigException; + // this returns a unique ID based on the db's connection URI and table prefix such that + // two different user pool IDs imply that the data for those two user pools are completely isolated. + String getUserPoolId(JsonObject jsonConfig); + + // if the input otherConfig has different values set for the same properties as this user pool's config, + // then this function should throw an error since this is a misconfig from ther user's side. + void assertThatConfigFromSameUserPoolIsNotConflicting(JsonObject otherConfig) throws InvalidConfigException; + void initFileLogging(String infoLogPath, String errorLogPath); void stopLogging(); From fa8af1313f448650ee119763c5f4a6edc2e26736 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 19 Jan 2023 22:16:58 +0530 Subject: [PATCH 04/93] exception throwing change --- src/main/java/io/supertokens/pluginInterface/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 734a89f4..f927c677 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -32,7 +32,7 @@ public interface Storage { // this returns a unique ID based on the db's connection URI and table prefix such that // two different user pool IDs imply that the data for those two user pools are completely isolated. - String getUserPoolId(JsonObject jsonConfig); + String getUserPoolId(JsonObject jsonConfig) throws InvalidConfigException; // if the input otherConfig has different values set for the same properties as this user pool's config, // then this function should throw an error since this is a misconfig from ther user's side. From faebf7af69ea05bc8755f60482f7d3a5e4c4f860 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 23 Jan 2023 13:19:51 +0530 Subject: [PATCH 05/93] adds function to get connection pool ID --- src/main/java/io/supertokens/pluginInterface/Storage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index f927c677..f9d09e30 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -34,6 +34,10 @@ public interface Storage { // two different user pool IDs imply that the data for those two user pools are completely isolated. String getUserPoolId(JsonObject jsonConfig) throws InvalidConfigException; + // this returns a unique ID based on the db's connection connection pool config. This can be different + // even if the getUserPoolId returns the same ID - based on the config provided by the user. + String getConnectionPoolId(JsonObject jsonConfig) throws InvalidConfigException; + // if the input otherConfig has different values set for the same properties as this user pool's config, // then this function should throw an error since this is a misconfig from ther user's side. void assertThatConfigFromSameUserPoolIsNotConflicting(JsonObject otherConfig) throws InvalidConfigException; From 102596d5cdd2286b31ed2f74c1ecfa0f87f78581 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 23 Jan 2023 14:23:36 +0530 Subject: [PATCH 06/93] changes to interface --- src/main/java/io/supertokens/pluginInterface/Storage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index f9d09e30..2eca985e 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -32,11 +32,11 @@ public interface Storage { // this returns a unique ID based on the db's connection URI and table prefix such that // two different user pool IDs imply that the data for those two user pools are completely isolated. - String getUserPoolId(JsonObject jsonConfig) throws InvalidConfigException; + String getUserPoolId(); // this returns a unique ID based on the db's connection connection pool config. This can be different // even if the getUserPoolId returns the same ID - based on the config provided by the user. - String getConnectionPoolId(JsonObject jsonConfig) throws InvalidConfigException; + String getConnectionPoolId(); // if the input otherConfig has different values set for the same properties as this user pool's config, // then this function should throw an error since this is a misconfig from ther user's side. From daa1089f8e02a79f043cffca289d35f5faeabb7d Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Tue, 24 Jan 2023 17:44:37 +0530 Subject: [PATCH 07/93] changes to initstorage interface function --- .../supertokens/pluginInterface/Storage.java | 3 +- .../exceptions/DbInitException.java | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/exceptions/DbInitException.java diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 2eca985e..ae688330 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -18,6 +18,7 @@ package io.supertokens.pluginInterface; import com.google.gson.JsonObject; +import io.supertokens.pluginInterface.exceptions.DbInitException; import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; @@ -47,7 +48,7 @@ public interface Storage { void stopLogging(); // load tables and create connection pools - void initStorage(); + void initStorage() throws DbInitException; // used by the core to do transactions the right way. STORAGE_TYPE getType(); diff --git a/src/main/java/io/supertokens/pluginInterface/exceptions/DbInitException.java b/src/main/java/io/supertokens/pluginInterface/exceptions/DbInitException.java new file mode 100644 index 00000000..f7d9de2a --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/exceptions/DbInitException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.exceptions; + +public class DbInitException extends Exception { + public DbInitException(Exception e) { + super(e); + } + + public DbInitException(String e) { + super(e); + } + + public DbInitException() { + super(); + } +} From c4efd7dd948c9f152d4a346a28732231f85a7788 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sat, 28 Jan 2023 14:37:29 +0530 Subject: [PATCH 08/93] adds function so that the core can create multiple user pools during testing across dbs --- src/main/java/io/supertokens/pluginInterface/Storage.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index ae688330..cbb1a4af 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -73,4 +73,7 @@ public interface Storage { // to be used for testing purposes only. This function will add dummy data to non-auth tables. void addInfoToNonAuthRecipesBasedOnUserId(String className, String userId) throws StorageQueryException; + // this function is used during testing in the core so that the core can + // create multiple user pools across any plugin being used. + void modifyConfigToAddANewUserPoolForTesting(JsonObject config, int poolNumber); } From b285355c6dae1dd71e42cb4b944f276743f712d8 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sun, 5 Feb 2023 20:27:14 +0530 Subject: [PATCH 09/93] adds tenantidentifier class --- .../multitenancy/TenantConfig.java | 8 +-- .../multitenancy/TenantIdentifier.java | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index d1960e28..4b9922e8 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -19,19 +19,17 @@ import com.google.gson.JsonObject; public class TenantConfig { - public String connectionUriDomain; - public String tenantId; + public TenantIdentifier tenantIdentifier; public EmailPasswordConfig emailPasswordConfig; public PasswordlessConfig passwordlessConfig; public ThirdPartyConfig thirdPartyConfig; public JsonObject coreConfig; - public TenantConfig(String connectionUriDomain, String tenantId, EmailPasswordConfig emailPasswordConfig, + public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig emailPasswordConfig, ThirdPartyConfig thirdPartyConfig, PasswordlessConfig passwordlessConfig, JsonObject coreConfig) { - this.connectionUriDomain = connectionUriDomain; - this.tenantId = tenantId; + this.tenantIdentifier = tenantIdentifier; this.coreConfig = coreConfig; this.emailPasswordConfig = emailPasswordConfig; this.passwordlessConfig = passwordlessConfig; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java new file mode 100644 index 00000000..d91e497f --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +public class TenantIdentifier { + private final String connectionUriDomain; + private final String tenantId; + private final String appId; + + public TenantIdentifier(String connectionUriDomain, String appId, String tenantId) { + this.connectionUriDomain = connectionUriDomain; + this.tenantId = tenantId; + this.appId = appId; + } + + public String getTenantId() { + if (this.tenantId == null || this.tenantId.equals("")) { + return "public"; + } + return this.tenantId.trim().toLowerCase(); + } + + public String getAppId() { + if (this.appId == null || this.appId.equals("")) { + return "public"; + } + return this.appId.trim().toLowerCase(); + } + + public String getConnectionUriDomain() { + if (this.connectionUriDomain == null) { + return ""; + } + return this.connectionUriDomain.trim().toLowerCase(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof TenantIdentifier) { + TenantIdentifier otherTenantIdentifier = (TenantIdentifier) other; + return otherTenantIdentifier.getTenantId().equals(this.getTenantId()) && + otherTenantIdentifier.getConnectionUriDomain().equals(this.getConnectionUriDomain()) && + otherTenantIdentifier.getAppId().equals(this.getAppId()); + } + return false; + } + + @Override + public int hashCode() { + return (this.getTenantId() + "|" + this.getConnectionUriDomain() + "|" + this.getAppId()).hashCode(); + } +} From 1227497ab0ce660ad0dad0fce6746baeb3bf4109 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Tue, 7 Feb 2023 11:50:49 +0530 Subject: [PATCH 10/93] adds more functions to interface --- .../pluginInterface/multitenancy/MultitenancyStorage.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index 7a24e04a..f79fdf17 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -26,9 +26,13 @@ public interface MultitenancyStorage extends Storage { void overwriteTenantConfig(TenantConfig config) throws UnknownTenantException; - void deleteTenant(String tenantId) throws UnknownTenantException; + void deleteTenant(TenantIdentifier tenantIdentifier) throws UnknownTenantException; - TenantConfig getTenantConfigForTenantId(String tenantId); + void deleteApp(TenantIdentifier tenantIdentifier) throws UnknownTenantException; + + void deleteConnectionUriDomainMapping(TenantIdentifier tenantIdentifier) throws UnknownTenantException; + + TenantConfig getTenantConfigForTenantIdentifier(TenantIdentifier tenantIdentifier); TenantConfig[] getAllTenants(); From 9eacc1e81037974435f3bb966b8206ece1ae8cd0 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Tue, 7 Feb 2023 12:40:41 +0530 Subject: [PATCH 11/93] removes unused exception --- .../QuitProgramFromPluginException.java | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/main/java/io/supertokens/pluginInterface/exceptions/QuitProgramFromPluginException.java diff --git a/src/main/java/io/supertokens/pluginInterface/exceptions/QuitProgramFromPluginException.java b/src/main/java/io/supertokens/pluginInterface/exceptions/QuitProgramFromPluginException.java deleted file mode 100644 index 7022fd63..00000000 --- a/src/main/java/io/supertokens/pluginInterface/exceptions/QuitProgramFromPluginException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ - -package io.supertokens.pluginInterface.exceptions; - -public class QuitProgramFromPluginException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public QuitProgramFromPluginException(String msg) { - super(msg); - } - - public QuitProgramFromPluginException(Exception e) { - super(e); - } -} From ee8993f25bc674ba913c38ea4a86bf0fbfae3a80 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 8 Feb 2023 17:34:12 +0530 Subject: [PATCH 12/93] small change --- .../pluginInterface/multitenancy/MultitenancyStorage.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index f79fdf17..d0283764 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -32,10 +32,6 @@ public interface MultitenancyStorage extends Storage { void deleteConnectionUriDomainMapping(TenantIdentifier tenantIdentifier) throws UnknownTenantException; - TenantConfig getTenantConfigForTenantIdentifier(TenantIdentifier tenantIdentifier); - TenantConfig[] getAllTenants(); - TenantConfig[] getAllTenantsWithThirdPartyId(String thirdPartyId); - } \ No newline at end of file From 6e100b87aba94bf94b41efcf92ac82663300f31f Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 8 Feb 2023 18:49:33 +0530 Subject: [PATCH 13/93] adds new functions --- .../pluginInterface/multitenancy/MultitenancyStorage.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index d0283764..cd44f1fe 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -17,8 +17,10 @@ package io.supertokens.pluginInterface.multitenancy; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; import io.supertokens.pluginInterface.multitenancy.exceptions.UnknownTenantException; +import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; public interface MultitenancyStorage extends Storage { @@ -34,4 +36,10 @@ public interface MultitenancyStorage extends Storage { TenantConfig[] getAllTenants(); + void addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws UnknownTenantException, + UnknownUserIdException; + + void addRoleToTenant(TenantIdentifier tenantIdentifier, String role) throws UnknownTenantException, + UnknownRoleException; + } \ No newline at end of file From b7b1b50dd3570f1cb27abe505ac9ea3660c41e7b Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Feb 2023 12:03:32 +0530 Subject: [PATCH 14/93] adds deletion functions for multitenancy --- .../multitenancy/MultitenancyStorage.java | 10 +++++++++ .../multitenancy/TenantConfig.java | 21 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index cd44f1fe..80c3f318 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -26,6 +26,12 @@ public interface MultitenancyStorage extends Storage { void createTenant(TenantConfig config) throws DuplicateTenantException; + // this adds tenantId to the target user pool + void addTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws DuplicateTenantException; + + // this also deletes all tenant info from all tables. + void deleteTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws UnknownTenantException; + void overwriteTenantConfig(TenantConfig config) throws UnknownTenantException; void deleteTenant(TenantIdentifier tenantIdentifier) throws UnknownTenantException; @@ -42,4 +48,8 @@ void addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws void addRoleToTenant(TenantIdentifier tenantIdentifier, String role) throws UnknownTenantException, UnknownRoleException; + void markAppIdAsDeleted(String appId) throws UnknownTenantException; + + void markConnectionUriDomainAsDeleted(String connectionUriDomain) throws UnknownTenantException; + } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index 4b9922e8..6ba968f7 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -20,6 +20,8 @@ public class TenantConfig { public TenantIdentifier tenantIdentifier; + public boolean appIdMarkedAsDeleted; + public boolean connectionUriDomainMarkedAsDeleted; public EmailPasswordConfig emailPasswordConfig; public PasswordlessConfig passwordlessConfig; public ThirdPartyConfig thirdPartyConfig; @@ -28,11 +30,28 @@ public class TenantConfig { public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig emailPasswordConfig, ThirdPartyConfig thirdPartyConfig, - PasswordlessConfig passwordlessConfig, JsonObject coreConfig) { + PasswordlessConfig passwordlessConfig, JsonObject coreConfig, boolean appIdMarkedAsDeleted, + boolean connectionUriDomainMarkedAsDeleted) { this.tenantIdentifier = tenantIdentifier; this.coreConfig = coreConfig; this.emailPasswordConfig = emailPasswordConfig; this.passwordlessConfig = passwordlessConfig; this.thirdPartyConfig = thirdPartyConfig; + this.appIdMarkedAsDeleted = appIdMarkedAsDeleted; + this.connectionUriDomainMarkedAsDeleted = connectionUriDomainMarkedAsDeleted; + } + + @Override + public boolean equals(Object other) { + if (other instanceof TenantConfig) { + TenantConfig otherTenantConfig = (TenantConfig) other; + return otherTenantConfig.tenantIdentifier.equals(this.tenantIdentifier); + } + return false; + } + + @Override + public int hashCode() { + return tenantIdentifier.hashCode(); } } From acb2b3ffb1eb784491428e39c2d51f342885ce14 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Feb 2023 13:51:07 +0530 Subject: [PATCH 15/93] few changes --- .../pluginInterface/multitenancy/TenantConfig.java | 12 ++++++++++++ .../multitenancy/TenantIdentifier.java | 9 ++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index 6ba968f7..f62cd1b3 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -41,6 +41,18 @@ public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig email this.connectionUriDomainMarkedAsDeleted = connectionUriDomainMarkedAsDeleted; } + public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig emailPasswordConfig, + ThirdPartyConfig thirdPartyConfig, + PasswordlessConfig passwordlessConfig, JsonObject coreConfig) { + this.tenantIdentifier = tenantIdentifier; + this.coreConfig = coreConfig; + this.emailPasswordConfig = emailPasswordConfig; + this.passwordlessConfig = passwordlessConfig; + this.thirdPartyConfig = thirdPartyConfig; + this.appIdMarkedAsDeleted = false; + this.connectionUriDomainMarkedAsDeleted = false; + } + @Override public boolean equals(Object other) { if (other instanceof TenantConfig) { diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java index d91e497f..de616248 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -17,6 +17,9 @@ package io.supertokens.pluginInterface.multitenancy; public class TenantIdentifier { + public static final String DEFAULT_TENANT_ID = "public"; + public static final String DEFAULT_APP_ID = "public"; + public static final String DEFAULT_CONNECTION_URI = ""; private final String connectionUriDomain; private final String tenantId; private final String appId; @@ -29,21 +32,21 @@ public TenantIdentifier(String connectionUriDomain, String appId, String tenantI public String getTenantId() { if (this.tenantId == null || this.tenantId.equals("")) { - return "public"; + return DEFAULT_TENANT_ID; } return this.tenantId.trim().toLowerCase(); } public String getAppId() { if (this.appId == null || this.appId.equals("")) { - return "public"; + return DEFAULT_APP_ID; } return this.appId.trim().toLowerCase(); } public String getConnectionUriDomain() { if (this.connectionUriDomain == null) { - return ""; + return DEFAULT_CONNECTION_URI; } return this.connectionUriDomain.trim().toLowerCase(); } From 8bde5553b03381f7647f87bba8076825dea24d5a Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 9 Feb 2023 20:10:15 +0530 Subject: [PATCH 16/93] updates exception class --- .../multitenancy/MultitenancyStorage.java | 20 +++++++++---------- .../exceptions/DuplicateTenantException.java | 4 +--- ...java => TenantOrAppNotFoundException.java} | 17 +++++++++++++--- 3 files changed, 25 insertions(+), 16 deletions(-) rename src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/{UnknownTenantException.java => TenantOrAppNotFoundException.java} (53%) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index 80c3f318..a73ff17f 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -19,7 +19,7 @@ import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; -import io.supertokens.pluginInterface.multitenancy.exceptions.UnknownTenantException; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; public interface MultitenancyStorage extends Storage { @@ -30,26 +30,26 @@ public interface MultitenancyStorage extends Storage { void addTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws DuplicateTenantException; // this also deletes all tenant info from all tables. - void deleteTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws UnknownTenantException; + void deleteTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; - void overwriteTenantConfig(TenantConfig config) throws UnknownTenantException; + void overwriteTenantConfig(TenantConfig config) throws TenantOrAppNotFoundException; - void deleteTenant(TenantIdentifier tenantIdentifier) throws UnknownTenantException; + void deleteTenant(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; - void deleteApp(TenantIdentifier tenantIdentifier) throws UnknownTenantException; + void deleteApp(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; - void deleteConnectionUriDomainMapping(TenantIdentifier tenantIdentifier) throws UnknownTenantException; + void deleteConnectionUriDomainMapping(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; TenantConfig[] getAllTenants(); - void addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws UnknownTenantException, + void addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws TenantOrAppNotFoundException, UnknownUserIdException; - void addRoleToTenant(TenantIdentifier tenantIdentifier, String role) throws UnknownTenantException, + void addRoleToTenant(TenantIdentifier tenantIdentifier, String role) throws TenantOrAppNotFoundException, UnknownRoleException; - void markAppIdAsDeleted(String appId) throws UnknownTenantException; + void markAppIdAsDeleted(String appId) throws TenantOrAppNotFoundException; - void markConnectionUriDomainAsDeleted(String connectionUriDomain) throws UnknownTenantException; + void markConnectionUriDomainAsDeleted(String connectionUriDomain) throws TenantOrAppNotFoundException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java index 86f04851..4555fa72 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateTenantException.java @@ -16,8 +16,6 @@ package io.supertokens.pluginInterface.multitenancy.exceptions; -import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; - -public class DuplicateTenantException extends EmailPasswordException { +public class DuplicateTenantException extends Exception { } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/UnknownTenantException.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/TenantOrAppNotFoundException.java similarity index 53% rename from src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/UnknownTenantException.java rename to src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/TenantOrAppNotFoundException.java index 49e1f23c..0bc67f5e 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/UnknownTenantException.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/TenantOrAppNotFoundException.java @@ -16,8 +16,19 @@ package io.supertokens.pluginInterface.multitenancy.exceptions; -import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -public class UnknownTenantException extends EmailPasswordException { - private static final long serialVersionUID = 7345237610253685511L; +public class TenantOrAppNotFoundException extends Exception { + private final TenantIdentifier tenantIdentifier; + + public TenantOrAppNotFoundException(TenantIdentifier tenantIdentifier) { + super("Tenant with the following connectionURIDomain, appId and tenantId combination not found: (" + + tenantIdentifier.getConnectionUriDomain() + + ", " + tenantIdentifier.getAppId() + ", " + tenantIdentifier.getTenantId() + ")"); + this.tenantIdentifier = tenantIdentifier; + } + + public TenantIdentifier getTenantIdentifier() { + return this.tenantIdentifier; + } } From 75f54a5cc25a8ba3586132d581b1ad80f658dea1 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Fri, 10 Feb 2023 14:33:49 +0530 Subject: [PATCH 17/93] simplifies delete of app and connectionuridomain --- .../multitenancy/TenantConfig.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index f62cd1b3..5a8b83c2 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -20,27 +20,12 @@ public class TenantConfig { public TenantIdentifier tenantIdentifier; - public boolean appIdMarkedAsDeleted; - public boolean connectionUriDomainMarkedAsDeleted; public EmailPasswordConfig emailPasswordConfig; public PasswordlessConfig passwordlessConfig; public ThirdPartyConfig thirdPartyConfig; public JsonObject coreConfig; - public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig emailPasswordConfig, - ThirdPartyConfig thirdPartyConfig, - PasswordlessConfig passwordlessConfig, JsonObject coreConfig, boolean appIdMarkedAsDeleted, - boolean connectionUriDomainMarkedAsDeleted) { - this.tenantIdentifier = tenantIdentifier; - this.coreConfig = coreConfig; - this.emailPasswordConfig = emailPasswordConfig; - this.passwordlessConfig = passwordlessConfig; - this.thirdPartyConfig = thirdPartyConfig; - this.appIdMarkedAsDeleted = appIdMarkedAsDeleted; - this.connectionUriDomainMarkedAsDeleted = connectionUriDomainMarkedAsDeleted; - } - public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig emailPasswordConfig, ThirdPartyConfig thirdPartyConfig, PasswordlessConfig passwordlessConfig, JsonObject coreConfig) { @@ -49,8 +34,6 @@ public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig email this.emailPasswordConfig = emailPasswordConfig; this.passwordlessConfig = passwordlessConfig; this.thirdPartyConfig = thirdPartyConfig; - this.appIdMarkedAsDeleted = false; - this.connectionUriDomainMarkedAsDeleted = false; } @Override From d1755a426b2baab3637aae0f6217afac5b8242b7 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sat, 11 Feb 2023 12:48:43 +0530 Subject: [PATCH 18/93] adds getters for certain tenant config props --- .../pluginInterface/multitenancy/TenantConfig.java | 9 ++++++++- .../pluginInterface/multitenancy/TenantIdentifier.java | 3 ++- .../pluginInterface/multitenancy/ThirdPartyConfig.java | 9 ++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index 5a8b83c2..7a9ce4f6 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -24,7 +24,7 @@ public class TenantConfig { public PasswordlessConfig passwordlessConfig; public ThirdPartyConfig thirdPartyConfig; - public JsonObject coreConfig; + private JsonObject coreConfig; public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig emailPasswordConfig, ThirdPartyConfig thirdPartyConfig, @@ -36,6 +36,13 @@ public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig email this.thirdPartyConfig = thirdPartyConfig; } + public JsonObject getCoreConfig() { + if (this.coreConfig == null) { + return new JsonObject(); + } + return coreConfig; + } + @Override public boolean equals(Object other) { if (other instanceof TenantConfig) { diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java index de616248..a0dbd0c8 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -64,6 +64,7 @@ public boolean equals(Object other) { @Override public int hashCode() { - return (this.getTenantId() + "|" + this.getConnectionUriDomain() + "|" + this.getAppId()).hashCode(); + return (this.getTenantId() + "|" + this.getConnectionUriDomain() + "|" + + this.getAppId()).hashCode(); } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index ac16e718..d04e82dd 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -20,13 +20,20 @@ public class ThirdPartyConfig { public boolean enabled; - public Provider[] providers; + private Provider[] providers; public ThirdPartyConfig(boolean enabled, Provider[] providers) { this.enabled = enabled; this.providers = providers; } + public Provider[] getProviders() { + if (this.providers == null) { + return new Provider[0]; + } + return providers; + } + public class Provider { public String thirdPartyId; public String name; From ac4af09c73ad5a0bc83fb80976a96a58cfd6c3a7 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sun, 12 Feb 2023 20:25:17 +0530 Subject: [PATCH 19/93] adds equals functions for tenantconfig types --- .../multitenancy/EmailPasswordConfig.java | 10 +++ .../multitenancy/PasswordlessConfig.java | 9 +++ .../multitenancy/TenantConfig.java | 11 +++ .../multitenancy/ThirdPartyConfig.java | 71 +++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java index fc00bb86..01d25929 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java @@ -22,4 +22,14 @@ public class EmailPasswordConfig { public EmailPasswordConfig(boolean enabled) { this.enabled = enabled; } + + @Override + public boolean equals(Object other) { + if (other instanceof EmailPasswordConfig) { + EmailPasswordConfig otherEmailPasswordConfig = (EmailPasswordConfig) other; + return otherEmailPasswordConfig.enabled == this.enabled; + } + return false; + } + } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java index dc629643..66db1977 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/PasswordlessConfig.java @@ -22,4 +22,13 @@ public class PasswordlessConfig { public PasswordlessConfig(boolean enabled) { this.enabled = enabled; } + + @Override + public boolean equals(Object other) { + if (other instanceof PasswordlessConfig) { + PasswordlessConfig otherPasswordlessConfig = (PasswordlessConfig) other; + return otherPasswordlessConfig.enabled == this.enabled; + } + return false; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index 7a9ce4f6..e23b3ed1 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -43,6 +43,17 @@ public JsonObject getCoreConfig() { return coreConfig; } + public boolean deepEquals(TenantConfig other) { + if (other == null) { + return false; + } + return this.tenantIdentifier.equals(other.tenantIdentifier) && + this.emailPasswordConfig.equals(other.emailPasswordConfig) && + this.passwordlessConfig.equals(other.passwordlessConfig) && + this.thirdPartyConfig.equals(other.thirdPartyConfig) && + this.getCoreConfig().equals(other.getCoreConfig()); + } + @Override public boolean equals(Object other) { if (other instanceof TenantConfig) { diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index d04e82dd..d4b4b1ed 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -18,6 +18,9 @@ import com.google.gson.JsonObject; +import java.util.Arrays; +import java.util.Objects; + public class ThirdPartyConfig { public boolean enabled; private Provider[] providers; @@ -49,6 +52,28 @@ public class Provider { public String oidcDiscoveryEndpoint; public boolean requireEmail; public UserInfoMap userInfoMap; + + @Override + public boolean equals(Object other) { + if (other instanceof Provider) { + Provider otherProvider = (Provider) other; + return otherProvider.thirdPartyId.equals(this.thirdPartyId) && + otherProvider.name.equals(this.name) && + Arrays.equals(otherProvider.clients, this.clients) && + otherProvider.authorizationEndpoint.equals(this.authorizationEndpoint) && + otherProvider.authorizationEndpointQueryParams.equals(this.authorizationEndpointQueryParams) && + otherProvider.tokenEndpoint.equals(this.tokenEndpoint) && + otherProvider.tokenEndpointBodyParams.equals(this.tokenEndpointBodyParams) && + otherProvider.userInfoEndpoint.equals(this.userInfoEndpoint) && + otherProvider.userInfoEndpointQueryParams.equals(this.userInfoEndpointQueryParams) && + otherProvider.userInfoEndpointHeaders.equals(this.userInfoEndpointHeaders) && + otherProvider.jwksURI.equals(this.jwksURI) && + otherProvider.oidcDiscoveryEndpoint.equals(this.oidcDiscoveryEndpoint) && + otherProvider.requireEmail == this.requireEmail && + otherProvider.userInfoMap.equals(this.userInfoMap); + } + return false; + } } public class ProviderClients { @@ -58,6 +83,20 @@ public class ProviderClients { public String[] scope; public boolean forcePKCE; public JsonObject additionalConfig; + + @Override + public boolean equals(Object other) { + if (other instanceof ProviderClients) { + ProviderClients otherProviderClients = (ProviderClients) other; + return otherProviderClients.clientType.equals(this.clientType) && + otherProviderClients.clientId.equals(this.clientId) && + otherProviderClients.clientSecret.equals(this.clientSecret) && + Arrays.equals(otherProviderClients.scope, this.scope) && + otherProviderClients.forcePKCE == this.forcePKCE && + otherProviderClients.additionalConfig.equals(this.additionalConfig); + } + return false; + } } public class UserInfoMap { @@ -68,6 +107,16 @@ public UserInfoMap(UserInfoMapKeyValue fromIdTokenPayload, UserInfoMapKeyValue f this.fromIdTokenPayload = fromIdTokenPayload; this.fromUserInfoAPI = fromUserInfoAPI; } + + @Override + public boolean equals(Object other) { + if (other instanceof UserInfoMap) { + UserInfoMap otherUserInfoMap = (UserInfoMap) other; + return Objects.equals(otherUserInfoMap.fromUserInfoAPI, this.fromUserInfoAPI) && + Objects.equals(otherUserInfoMap.fromIdTokenPayload, this.fromIdTokenPayload); + } + return false; + } } public class UserInfoMapKeyValue { @@ -80,5 +129,27 @@ public UserInfoMapKeyValue(String userId, String email, String emailVerified) { this.email = email; this.emailVerified = emailVerified; } + + @Override + public boolean equals(Object other) { + if (other instanceof UserInfoMapKeyValue) { + UserInfoMapKeyValue otherUserInfoMapKeyValue = (UserInfoMapKeyValue) other; + return otherUserInfoMapKeyValue.userId.equals(this.userId) && + otherUserInfoMapKeyValue.email.equals(this.email) && + otherUserInfoMapKeyValue.emailVerified.equals(this.emailVerified); + } + return false; + } + } + + @Override + public boolean equals(Object other) { + if (other instanceof ThirdPartyConfig) { + ThirdPartyConfig otherThirdPartyConfig = (ThirdPartyConfig) other; + return otherThirdPartyConfig.enabled == this.enabled && + Arrays.equals(otherThirdPartyConfig.getProviders(), this.getProviders()); + } + return false; } + } From 2425d1871fa1cd750a0f9251a8b80a9c8846857f Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sun, 12 Feb 2023 20:34:49 +0530 Subject: [PATCH 20/93] adds constructors for thirdparty config objects --- .../multitenancy/ThirdPartyConfig.java | 76 ++++++++++++++----- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index d4b4b1ed..51644be2 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -27,7 +27,7 @@ public class ThirdPartyConfig { public ThirdPartyConfig(boolean enabled, Provider[] providers) { this.enabled = enabled; - this.providers = providers; + this.providers = providers == null ? new Provider[0] : providers; } public Provider[] getProviders() { @@ -38,20 +38,44 @@ public Provider[] getProviders() { } public class Provider { - public String thirdPartyId; - public String name; - public ProviderClients[] clients; - public String authorizationEndpoint; - public JsonObject authorizationEndpointQueryParams; - public String tokenEndpoint; - public JsonObject tokenEndpointBodyParams; - public String userInfoEndpoint; - public JsonObject userInfoEndpointQueryParams; - public JsonObject userInfoEndpointHeaders; - public String jwksURI; - public String oidcDiscoveryEndpoint; - public boolean requireEmail; - public UserInfoMap userInfoMap; + private final String thirdPartyId; + private final String name; + private final ProviderClients[] clients; + private final String authorizationEndpoint; + private final JsonObject authorizationEndpointQueryParams; + private final String tokenEndpoint; + private final JsonObject tokenEndpointBodyParams; + private final String userInfoEndpoint; + private JsonObject userInfoEndpointQueryParams; + private final JsonObject userInfoEndpointHeaders; + private final String jwksURI; + private final String oidcDiscoveryEndpoint; + public final boolean requireEmail; + private final UserInfoMap userInfoMap; + + public Provider(String thirdPartyId, String name, ProviderClients[] clients, String authorizationEndpoint, + JsonObject authorizationEndpointQueryParams, String tokenEndpoint, + JsonObject tokenEndpointBodyParams, + String userInfoEndpoint, JsonObject userInfoEndpointQueryParams, + JsonObject userInfoEndpointHeaders, + String jwksURI, String oidcDiscoveryEndpoint, boolean requireEmail, UserInfoMap userInfoMap) { + this.thirdPartyId = thirdPartyId; + this.name = name; + this.clients = clients == null ? new ProviderClients[0] : clients; + this.authorizationEndpoint = authorizationEndpoint == null ? "" : authorizationEndpoint; + this.authorizationEndpointQueryParams = + authorizationEndpointQueryParams == null ? new JsonObject() : authorizationEndpointQueryParams; + this.tokenEndpoint = tokenEndpoint == null ? "" : tokenEndpoint; + this.tokenEndpointBodyParams = tokenEndpointBodyParams == null ? new JsonObject() : tokenEndpointBodyParams; + this.userInfoEndpoint = userInfoEndpoint == null ? "" : userInfoEndpoint; + this.userInfoEndpointQueryParams = + userInfoEndpointQueryParams == null ? new JsonObject() : userInfoEndpointQueryParams; + this.userInfoEndpointHeaders = userInfoEndpointHeaders == null ? new JsonObject() : userInfoEndpointHeaders; + this.jwksURI = jwksURI == null ? "" : jwksURI; + this.oidcDiscoveryEndpoint = oidcDiscoveryEndpoint == null ? "" : oidcDiscoveryEndpoint; + this.requireEmail = requireEmail; + this.userInfoMap = userInfoMap == null ? new UserInfoMap(null, null) : userInfoMap; + } @Override public boolean equals(Object other) { @@ -77,12 +101,22 @@ public boolean equals(Object other) { } public class ProviderClients { - public String clientType; - public String clientId; - public String clientSecret; - public String[] scope; - public boolean forcePKCE; - public JsonObject additionalConfig; + private final String clientType; + private final String clientId; + private final String clientSecret; + private final String[] scope; + private final boolean forcePKCE; + private final JsonObject additionalConfig; + + public ProviderClients(String clientType, String clientId, String clientSecret, String[] scope, + boolean forcePKCE, JsonObject additionalConfig) { + this.clientType = clientType == null ? "" : clientType; + this.clientId = clientId == null ? "" : clientId; + this.clientSecret = clientSecret == null ? "" : clientSecret; + this.scope = scope == null ? new String[0] : scope; + this.forcePKCE = forcePKCE; + this.additionalConfig = additionalConfig == null ? new JsonObject() : additionalConfig; + } @Override public boolean equals(Object other) { From efac64205f983a21b33a7f1bf99f973970cf34b1 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sun, 12 Feb 2023 22:19:13 +0530 Subject: [PATCH 21/93] changes equals for tenantconfig --- .../multitenancy/TenantConfig.java | 39 ++-- .../multitenancy/TenantIdentifier.java | 14 +- .../multitenancy/ThirdPartyConfig.java | 199 +++++++++++------- 3 files changed, 158 insertions(+), 94 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index e23b3ed1..0b35acbb 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -18,31 +18,36 @@ import com.google.gson.JsonObject; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public class TenantConfig { - public TenantIdentifier tenantIdentifier; - public EmailPasswordConfig emailPasswordConfig; - public PasswordlessConfig passwordlessConfig; - public ThirdPartyConfig thirdPartyConfig; - private JsonObject coreConfig; + @Nonnull + public transient final TenantIdentifier tenantIdentifier; + + @Nonnull + public final EmailPasswordConfig emailPasswordConfig; + + @Nonnull + public final PasswordlessConfig passwordlessConfig; + + @Nonnull + public final ThirdPartyConfig thirdPartyConfig; - public TenantConfig(TenantIdentifier tenantIdentifier, EmailPasswordConfig emailPasswordConfig, - ThirdPartyConfig thirdPartyConfig, - PasswordlessConfig passwordlessConfig, JsonObject coreConfig) { + @Nonnull + public final JsonObject coreConfig; + + public TenantConfig(@Nonnull TenantIdentifier tenantIdentifier, @Nonnull EmailPasswordConfig emailPasswordConfig, + @Nonnull ThirdPartyConfig thirdPartyConfig, + @Nonnull PasswordlessConfig passwordlessConfig, @Nullable JsonObject coreConfig) { this.tenantIdentifier = tenantIdentifier; - this.coreConfig = coreConfig; + this.coreConfig = coreConfig == null ? new JsonObject() : coreConfig; this.emailPasswordConfig = emailPasswordConfig; this.passwordlessConfig = passwordlessConfig; this.thirdPartyConfig = thirdPartyConfig; } - public JsonObject getCoreConfig() { - if (this.coreConfig == null) { - return new JsonObject(); - } - return coreConfig; - } - public boolean deepEquals(TenantConfig other) { if (other == null) { return false; @@ -51,7 +56,7 @@ public boolean deepEquals(TenantConfig other) { this.emailPasswordConfig.equals(other.emailPasswordConfig) && this.passwordlessConfig.equals(other.passwordlessConfig) && this.thirdPartyConfig.equals(other.thirdPartyConfig) && - this.getCoreConfig().equals(other.getCoreConfig()); + this.coreConfig.equals(other.coreConfig); } @Override diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java index a0dbd0c8..f7f754f6 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -16,20 +16,30 @@ package io.supertokens.pluginInterface.multitenancy; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public class TenantIdentifier { public static final String DEFAULT_TENANT_ID = "public"; public static final String DEFAULT_APP_ID = "public"; public static final String DEFAULT_CONNECTION_URI = ""; + + @Nullable private final String connectionUriDomain; + + @Nullable private final String tenantId; + + @Nullable private final String appId; - public TenantIdentifier(String connectionUriDomain, String appId, String tenantId) { + public TenantIdentifier(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable String tenantId) { this.connectionUriDomain = connectionUriDomain; this.tenantId = tenantId; this.appId = appId; } + @Nonnull public String getTenantId() { if (this.tenantId == null || this.tenantId.equals("")) { return DEFAULT_TENANT_ID; @@ -37,6 +47,7 @@ public String getTenantId() { return this.tenantId.trim().toLowerCase(); } + @Nonnull public String getAppId() { if (this.appId == null || this.appId.equals("")) { return DEFAULT_APP_ID; @@ -44,6 +55,7 @@ public String getAppId() { return this.appId.trim().toLowerCase(); } + @Nonnull public String getConnectionUriDomain() { if (this.connectionUriDomain == null) { return DEFAULT_CONNECTION_URI; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index 51644be2..29efe5ba 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -18,63 +18,88 @@ import com.google.gson.JsonObject; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.Objects; public class ThirdPartyConfig { - public boolean enabled; - private Provider[] providers; + public final boolean enabled; - public ThirdPartyConfig(boolean enabled, Provider[] providers) { + @Nonnull + public final Provider[] providers; + + public ThirdPartyConfig(boolean enabled, @Nullable Provider[] providers) { this.enabled = enabled; this.providers = providers == null ? new Provider[0] : providers; } - public Provider[] getProviders() { - if (this.providers == null) { - return new Provider[0]; - } - return providers; - } + public static class Provider { + + @Nonnull + public final String thirdPartyId; + + @Nonnull + public final String name; + + @Nullable + public final ProviderClients[] clients; + + @Nullable + public final String authorizationEndpoint; + + @Nullable + public final JsonObject authorizationEndpointQueryParams; + + @Nullable + public final String tokenEndpoint; + + @Nullable + public final JsonObject tokenEndpointBodyParams; + + @Nullable + public final String userInfoEndpoint; + + @Nullable + public JsonObject userInfoEndpointQueryParams; + + @Nullable + public final JsonObject userInfoEndpointHeaders; - public class Provider { - private final String thirdPartyId; - private final String name; - private final ProviderClients[] clients; - private final String authorizationEndpoint; - private final JsonObject authorizationEndpointQueryParams; - private final String tokenEndpoint; - private final JsonObject tokenEndpointBodyParams; - private final String userInfoEndpoint; - private JsonObject userInfoEndpointQueryParams; - private final JsonObject userInfoEndpointHeaders; - private final String jwksURI; - private final String oidcDiscoveryEndpoint; + @Nullable + public final String jwksURI; + + @Nullable + public final String oidcDiscoveryEndpoint; + + @Nullable public final boolean requireEmail; - private final UserInfoMap userInfoMap; - - public Provider(String thirdPartyId, String name, ProviderClients[] clients, String authorizationEndpoint, - JsonObject authorizationEndpointQueryParams, String tokenEndpoint, - JsonObject tokenEndpointBodyParams, - String userInfoEndpoint, JsonObject userInfoEndpointQueryParams, - JsonObject userInfoEndpointHeaders, - String jwksURI, String oidcDiscoveryEndpoint, boolean requireEmail, UserInfoMap userInfoMap) { + + @Nullable + public final UserInfoMap userInfoMap; + + public Provider(@Nonnull String thirdPartyId, @Nonnull String name, @Nullable ProviderClients[] clients, + @Nullable String authorizationEndpoint, + @Nullable JsonObject authorizationEndpointQueryParams, @Nullable String tokenEndpoint, + @Nullable JsonObject tokenEndpointBodyParams, + @Nullable String userInfoEndpoint, @Nullable JsonObject userInfoEndpointQueryParams, + @Nullable JsonObject userInfoEndpointHeaders, + @Nullable String jwksURI, @Nullable String oidcDiscoveryEndpoint, boolean requireEmail, + @Nullable UserInfoMap userInfoMap) { this.thirdPartyId = thirdPartyId; this.name = name; - this.clients = clients == null ? new ProviderClients[0] : clients; - this.authorizationEndpoint = authorizationEndpoint == null ? "" : authorizationEndpoint; - this.authorizationEndpointQueryParams = - authorizationEndpointQueryParams == null ? new JsonObject() : authorizationEndpointQueryParams; - this.tokenEndpoint = tokenEndpoint == null ? "" : tokenEndpoint; - this.tokenEndpointBodyParams = tokenEndpointBodyParams == null ? new JsonObject() : tokenEndpointBodyParams; - this.userInfoEndpoint = userInfoEndpoint == null ? "" : userInfoEndpoint; - this.userInfoEndpointQueryParams = - userInfoEndpointQueryParams == null ? new JsonObject() : userInfoEndpointQueryParams; - this.userInfoEndpointHeaders = userInfoEndpointHeaders == null ? new JsonObject() : userInfoEndpointHeaders; - this.jwksURI = jwksURI == null ? "" : jwksURI; - this.oidcDiscoveryEndpoint = oidcDiscoveryEndpoint == null ? "" : oidcDiscoveryEndpoint; + this.clients = clients; + this.authorizationEndpoint = authorizationEndpoint; + this.authorizationEndpointQueryParams = authorizationEndpointQueryParams; + this.tokenEndpoint = tokenEndpoint; + this.tokenEndpointBodyParams = tokenEndpointBodyParams; + this.userInfoEndpoint = userInfoEndpoint; + this.userInfoEndpointQueryParams = userInfoEndpointQueryParams; + this.userInfoEndpointHeaders = userInfoEndpointHeaders; + this.jwksURI = jwksURI; + this.oidcDiscoveryEndpoint = oidcDiscoveryEndpoint; this.requireEmail = requireEmail; - this.userInfoMap = userInfoMap == null ? new UserInfoMap(null, null) : userInfoMap; + this.userInfoMap = userInfoMap; } @Override @@ -84,60 +109,77 @@ public boolean equals(Object other) { return otherProvider.thirdPartyId.equals(this.thirdPartyId) && otherProvider.name.equals(this.name) && Arrays.equals(otherProvider.clients, this.clients) && - otherProvider.authorizationEndpoint.equals(this.authorizationEndpoint) && - otherProvider.authorizationEndpointQueryParams.equals(this.authorizationEndpointQueryParams) && - otherProvider.tokenEndpoint.equals(this.tokenEndpoint) && - otherProvider.tokenEndpointBodyParams.equals(this.tokenEndpointBodyParams) && - otherProvider.userInfoEndpoint.equals(this.userInfoEndpoint) && - otherProvider.userInfoEndpointQueryParams.equals(this.userInfoEndpointQueryParams) && - otherProvider.userInfoEndpointHeaders.equals(this.userInfoEndpointHeaders) && - otherProvider.jwksURI.equals(this.jwksURI) && - otherProvider.oidcDiscoveryEndpoint.equals(this.oidcDiscoveryEndpoint) && + Objects.equals(otherProvider.authorizationEndpoint, this.authorizationEndpoint) && + Objects.equals(otherProvider.authorizationEndpointQueryParams, + this.authorizationEndpointQueryParams) && + Objects.equals(otherProvider.tokenEndpoint, this.tokenEndpoint) && + Objects.equals(otherProvider.tokenEndpointBodyParams, this.tokenEndpointBodyParams) && + Objects.equals(otherProvider.userInfoEndpoint, this.userInfoEndpoint) && + Objects.equals(otherProvider.userInfoEndpointQueryParams, this.userInfoEndpointQueryParams) && + Objects.equals(otherProvider.userInfoEndpointHeaders, this.userInfoEndpointHeaders) && + Objects.equals(otherProvider.jwksURI, this.jwksURI) && + Objects.equals(otherProvider.oidcDiscoveryEndpoint, this.oidcDiscoveryEndpoint) && otherProvider.requireEmail == this.requireEmail && - otherProvider.userInfoMap.equals(this.userInfoMap); + Objects.equals(otherProvider.userInfoMap, this.userInfoMap); } return false; } } - public class ProviderClients { - private final String clientType; - private final String clientId; - private final String clientSecret; - private final String[] scope; - private final boolean forcePKCE; - private final JsonObject additionalConfig; - - public ProviderClients(String clientType, String clientId, String clientSecret, String[] scope, - boolean forcePKCE, JsonObject additionalConfig) { - this.clientType = clientType == null ? "" : clientType; - this.clientId = clientId == null ? "" : clientId; - this.clientSecret = clientSecret == null ? "" : clientSecret; - this.scope = scope == null ? new String[0] : scope; + public static class ProviderClients { + + @Nullable + public final String clientType; + + @Nonnull + public final String clientId; + + @Nullable + public final String clientSecret; + + @Nullable + public final String[] scope; + + public final boolean forcePKCE; + + @Nullable + public final JsonObject additionalConfig; + + public ProviderClients(@Nullable String clientType, @Nonnull String clientId, @Nullable String clientSecret, + @Nullable String[] scope, + boolean forcePKCE, @Nullable JsonObject additionalConfig) { + this.clientType = clientType; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.scope = scope; this.forcePKCE = forcePKCE; - this.additionalConfig = additionalConfig == null ? new JsonObject() : additionalConfig; + this.additionalConfig = additionalConfig; } @Override public boolean equals(Object other) { if (other instanceof ProviderClients) { ProviderClients otherProviderClients = (ProviderClients) other; - return otherProviderClients.clientType.equals(this.clientType) && + return Objects.equals(otherProviderClients.clientType, this.clientType) && otherProviderClients.clientId.equals(this.clientId) && - otherProviderClients.clientSecret.equals(this.clientSecret) && + Objects.equals(otherProviderClients.clientSecret, this.clientSecret) && Arrays.equals(otherProviderClients.scope, this.scope) && otherProviderClients.forcePKCE == this.forcePKCE && - otherProviderClients.additionalConfig.equals(this.additionalConfig); + Objects.equals(otherProviderClients.additionalConfig, this.additionalConfig); } return false; } } - public class UserInfoMap { + public static class UserInfoMap { + @Nullable public UserInfoMapKeyValue fromIdTokenPayload; + + @Nullable public UserInfoMapKeyValue fromUserInfoAPI; - public UserInfoMap(UserInfoMapKeyValue fromIdTokenPayload, UserInfoMapKeyValue fromUserInfoAPI) { + public UserInfoMap(@Nullable UserInfoMapKeyValue fromIdTokenPayload, + @Nullable UserInfoMapKeyValue fromUserInfoAPI) { this.fromIdTokenPayload = fromIdTokenPayload; this.fromUserInfoAPI = fromUserInfoAPI; } @@ -153,12 +195,17 @@ public boolean equals(Object other) { } } - public class UserInfoMapKeyValue { + public static class UserInfoMapKeyValue { + @Nonnull public String userId; + + @Nonnull public String email; + + @Nonnull public String emailVerified; - public UserInfoMapKeyValue(String userId, String email, String emailVerified) { + public UserInfoMapKeyValue(@Nonnull String userId, @Nonnull String email, @Nonnull String emailVerified) { this.userId = userId; this.email = email; this.emailVerified = emailVerified; @@ -181,7 +228,7 @@ public boolean equals(Object other) { if (other instanceof ThirdPartyConfig) { ThirdPartyConfig otherThirdPartyConfig = (ThirdPartyConfig) other; return otherThirdPartyConfig.enabled == this.enabled && - Arrays.equals(otherThirdPartyConfig.getProviders(), this.getProviders()); + Arrays.equals(otherThirdPartyConfig.providers, this.providers); } return false; } From 18dcc9ae9e9c1cf188c2c0db11316993b748d179 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sun, 12 Feb 2023 22:22:15 +0530 Subject: [PATCH 22/93] removes nullable annotation from primitive type --- .../pluginInterface/multitenancy/ThirdPartyConfig.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index 29efe5ba..1237ac38 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -72,7 +72,6 @@ public static class Provider { @Nullable public final String oidcDiscoveryEndpoint; - @Nullable public final boolean requireEmail; @Nullable From d402f2d3679c91ed67b632ff6720c30609a1ea60 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 13 Feb 2023 17:41:16 +0530 Subject: [PATCH 23/93] adds tenantIdentifier for emailpassword and useridmapping recipes --- .../authRecipe/AuthRecipeStorage.java | 12 +++++--- .../emailpassword/EmailPasswordStorage.java | 30 +++++++------------ .../useridmapping/UserIdMappingStorage.java | 20 ++++++++----- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java index 48fbc962..1f30e276 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java @@ -20,17 +20,21 @@ import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import javax.annotation.Nonnull; import javax.annotation.Nullable; public interface AuthRecipeStorage extends Storage { - long getUsersCount(@Nullable RECIPE_ID[] includeRecipeIds) throws StorageQueryException; + long getUsersCount(TenantIdentifier tenantIdentifier, @Nullable RECIPE_ID[] includeRecipeIds) + throws StorageQueryException; - AuthRecipeUserInfo[] getUsers(@Nonnull Integer limit, @Nonnull String timeJoinedOrder, - @Nullable RECIPE_ID[] includeRecipeIds, @Nullable String userId, @Nullable Long timeJoined) + AuthRecipeUserInfo[] getUsers(TenantIdentifier tenantIdentifier, @Nonnull Integer limit, + @Nonnull String timeJoinedOrder, + @Nullable RECIPE_ID[] includeRecipeIds, @Nullable String userId, + @Nullable Long timeJoined) throws StorageQueryException; - boolean doesUserIdExist(String userId) throws StorageQueryException; + boolean doesUserIdExist(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index 1bb1c05d..005ddaca 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -22,36 +22,28 @@ import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; - -import javax.annotation.Nonnull; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; public interface EmailPasswordStorage extends AuthRecipeStorage { - void signUp(UserInfo userInfo) throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException; + void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) + throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException; - void deleteEmailPasswordUser(String userId) throws StorageQueryException; + void deleteEmailPasswordUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; - UserInfo getUserInfoUsingId(String id) throws StorageQueryException; + UserInfo getUserInfoUsingId(TenantIdentifier tenantIdentifier, String id) throws StorageQueryException; - UserInfo getUserInfoUsingEmail(String email) throws StorageQueryException; + UserInfo getUserInfoUsingEmail(TenantIdentifier tenantIdentifier, String email) throws StorageQueryException; - void addPasswordResetToken(PasswordResetTokenInfo passwordResetTokenInfo) + void addPasswordResetToken(TenantIdentifier tenantIdentifier, PasswordResetTokenInfo passwordResetTokenInfo) throws StorageQueryException, UnknownUserIdException, DuplicatePasswordResetTokenException; - PasswordResetTokenInfo getPasswordResetTokenInfo(String token) throws StorageQueryException; + PasswordResetTokenInfo getPasswordResetTokenInfo(TenantIdentifier tenantIdentifier, String token) + throws StorageQueryException; void deleteExpiredPasswordResetTokens() throws StorageQueryException; - PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser(String userId) throws StorageQueryException; - - @Deprecated - UserInfo[] getUsers(@Nonnull String userId, @Nonnull Long timeJoined, @Nonnull Integer limit, - @Nonnull String timeJoinedOrder) throws StorageQueryException; - - @Deprecated - UserInfo[] getUsers(@Nonnull Integer limit, @Nonnull String timeJoinedOrder) throws StorageQueryException; - - @Deprecated - long getUsersCount() throws StorageQueryException; + PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser(TenantIdentifier tenantIdentifier, String userId) + throws StorageQueryException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java index 2f68c51c..a5cd1d58 100644 --- a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.useridmapping.exception.UnknownSuperTokensUserIdException; import io.supertokens.pluginInterface.useridmapping.exception.UserIdMappingAlreadyExistsException; @@ -27,21 +28,26 @@ public interface UserIdMappingStorage extends Storage { - void createUserIdMapping(String superTokensUserId, String externalUserId, @Nullable String externalUserIdInfo) + void createUserIdMapping(TenantIdentifier tenantIdentifier, String superTokensUserId, String externalUserId, + @Nullable String externalUserIdInfo) throws StorageQueryException, UnknownSuperTokensUserIdException, UserIdMappingAlreadyExistsException; - boolean deleteUserIdMapping(String userId, boolean isSuperTokensUserId) throws StorageQueryException; + boolean deleteUserIdMapping(TenantIdentifier tenantIdentifier, String userId, boolean isSuperTokensUserId) + throws StorageQueryException; - UserIdMapping getUserIdMapping(String userId, boolean isSuperTokensUserId) throws StorageQueryException; + UserIdMapping getUserIdMapping(TenantIdentifier tenantIdentifier, String userId, boolean isSuperTokensUserId) + throws StorageQueryException; - UserIdMapping[] getUserIdMapping(String userId) throws StorageQueryException; + UserIdMapping[] getUserIdMapping(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; - boolean updateOrDeleteExternalUserIdInfo(String userId, boolean isSuperTokensUserId, - @Nullable String externalUserIdInfo) throws StorageQueryException; + boolean updateOrDeleteExternalUserIdInfo(TenantIdentifier tenantIdentifier, String userId, + boolean isSuperTokensUserId, + @Nullable String externalUserIdInfo) throws StorageQueryException; // This function will be used to retrieve the userId mapping for a list of userIds. The key of the HashMap will be // superTokensUserId and the value will be the externalUserId. If a mapping does not exist for an input userId, // it will not be in a part of the returned HashMap - HashMap getUserIdMappingForSuperTokensIds(ArrayList userIds) throws StorageQueryException; + HashMap getUserIdMappingForSuperTokensIds(TenantIdentifier tenantIdentifier, + ArrayList userIds) throws StorageQueryException; } From 0b15606e3ed035f30aab5b49d30293dc73a167dd Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 13 Feb 2023 19:03:00 +0530 Subject: [PATCH 24/93] adds extra comment --- .../pluginInterface/useridmapping/UserIdMappingStorage.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java index a5cd1d58..36ca648f 100644 --- a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java @@ -28,6 +28,9 @@ public interface UserIdMappingStorage extends Storage { + // whilst these take the full tenantIdentifier as an input, they ignore the tenantId + // cause user ID mapping is per app and not per tenant. + void createUserIdMapping(TenantIdentifier tenantIdentifier, String superTokensUserId, String externalUserId, @Nullable String externalUserIdInfo) throws StorageQueryException, UnknownSuperTokensUserIdException, UserIdMappingAlreadyExistsException; From 2a4c598cb94324a9be3a4d9077375b17edd3773e Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 13 Feb 2023 22:21:30 +0530 Subject: [PATCH 25/93] adds comment --- .../pluginInterface/emailpassword/EmailPasswordStorage.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index 005ddaca..ec19a9bc 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -41,6 +41,8 @@ void addPasswordResetToken(TenantIdentifier tenantIdentifier, PasswordResetToken PasswordResetTokenInfo getPasswordResetTokenInfo(TenantIdentifier tenantIdentifier, String token) throws StorageQueryException; + // we purposely do not add TenantIdentifier to this query cause + // this is called from a cronjob that runs per user pool ID void deleteExpiredPasswordResetTokens() throws StorageQueryException; PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser(TenantIdentifier tenantIdentifier, String userId) From 54e2a2993d645c90d405145c8de68058137cce67 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Tue, 14 Feb 2023 17:24:08 +0530 Subject: [PATCH 26/93] changes to incorporate tenantIndetifier for key value storage --- src/main/java/io/supertokens/pluginInterface/Storage.java | 8 +++++--- .../pluginInterface/sqlStorage/SQLStorage.java | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index cbb1a4af..8b80693b 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -21,6 +21,7 @@ import io.supertokens.pluginInterface.exceptions.DbInitException; import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import java.util.Set; @@ -58,9 +59,9 @@ public interface Storage { void close(); - KeyValueInfo getKeyValue(String key) throws StorageQueryException; + KeyValueInfo getKeyValue(TenantIdentifier tenantIdentifier, String key) throws StorageQueryException; - void setKeyValue(String key, KeyValueInfo info) throws StorageQueryException; + void setKeyValue(TenantIdentifier tenantIdentifier, String key, KeyValueInfo info) throws StorageQueryException; void setStorageLayerEnabled(boolean enabled); @@ -68,7 +69,8 @@ public interface Storage { // this function will be used in the createUserIdMapping and deleteUserIdMapping functions to check if the userId is // being used in NonAuth recipes. - boolean isUserIdBeingUsedInNonAuthRecipe(String className, String userId) throws StorageQueryException; + boolean isUserIdBeingUsedInNonAuthRecipe(TenantIdentifier tenantIdentifier, String className, String userId) + throws StorageQueryException; // to be used for testing purposes only. This function will add dummy data to non-auth tables. void addInfoToNonAuthRecipesBasedOnUserId(String className, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java index c4b33ec2..e8d5e919 100644 --- a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java @@ -21,6 +21,7 @@ import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; public interface SQLStorage extends Storage { T startTransaction(TransactionLogic logic, TransactionIsolationLevel isolationLevel) @@ -30,9 +31,11 @@ T startTransaction(TransactionLogic logic, TransactionIsolationLevel isol void commitTransaction(TransactionConnection con) throws StorageQueryException; - void setKeyValue_Transaction(TransactionConnection con, String key, KeyValueInfo info) throws StorageQueryException; + void setKeyValue_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String key, + KeyValueInfo info) throws StorageQueryException; - KeyValueInfo getKeyValue_Transaction(TransactionConnection con, String key) throws StorageQueryException; + KeyValueInfo getKeyValue_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String key) + throws StorageQueryException; interface TransactionLogic { T mainLogicAndCommit(TransactionConnection con) throws StorageQueryException, StorageTransactionLogicException; From dfb936300d99339ec8ad1f0846be8aa280bcedd0 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Tue, 14 Feb 2023 22:53:09 +0530 Subject: [PATCH 27/93] changes to session receipe to add tenantIdentifier --- .../session/SessionStorage.java | 25 ++++++++++------- .../session/sqlStorage/SessionSQLStorage.java | 27 ++++++++++++------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java index 638f2462..c51da54c 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java @@ -17,31 +17,36 @@ package io.supertokens.pluginInterface.session; import com.google.gson.JsonObject; -import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; import javax.annotation.Nullable; public interface SessionStorage extends NonAuthRecipeStorage { - void createNewSession(String sessionHandle, String userId, String refreshTokenHash2, JsonObject userDataInDatabase, - long expiry, JsonObject userDataInJWT, long createdAtTime) throws StorageQueryException; + void createNewSession(TenantIdentifier tenantIdentifier, String sessionHandle, String userId, + String refreshTokenHash2, JsonObject userDataInDatabase, + long expiry, JsonObject userDataInJWT, long createdAtTime) throws StorageQueryException; - void deleteSessionsOfUser(String userId) throws StorageQueryException; + void deleteSessionsOfUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; // return number of rows else throw UnsupportedOperationException - int getNumberOfSessions() throws StorageQueryException; + int getNumberOfSessions(TenantIdentifier tenantIdentifier) throws StorageQueryException; - int deleteSession(String[] sessionHandles) throws StorageQueryException; + int deleteSession(TenantIdentifier tenantIdentifier, String[] sessionHandles) throws StorageQueryException; - String[] getAllNonExpiredSessionHandlesForUser(String userId) throws StorageQueryException; + String[] getAllNonExpiredSessionHandlesForUser(TenantIdentifier tenantIdentifier, String userId) + throws StorageQueryException; + // we purposely do not add TenantIdentifier to this query cause + // this is called from a cronjob that runs per user pool ID void deleteAllExpiredSessions() throws StorageQueryException; - SessionInfo getSession(String sessionHandle) throws StorageQueryException; + SessionInfo getSession(TenantIdentifier tenantIdentifier, String sessionHandle) throws StorageQueryException; - int updateSession(String sessionHandle, @Nullable JsonObject sessionData, @Nullable JsonObject jwtPayload) + int updateSession(TenantIdentifier tenantIdentifier, String sessionHandle, @Nullable JsonObject sessionData, + @Nullable JsonObject jwtPayload) throws StorageQueryException; - void removeAccessTokenSigningKeysBefore(long time) throws StorageQueryException; + void removeAccessTokenSigningKeysBefore(TenantIdentifier tenantIdentifier, long time) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java index 003b92c7..f98d98b5 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.KeyValueInfo; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.session.SessionInfo; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; @@ -25,23 +26,31 @@ public interface SessionSQLStorage extends SessionStorage, SQLStorage { - void removeLegacyAccessTokenSigningKey_Transaction(TransactionConnection con) throws StorageQueryException; + void removeLegacyAccessTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con) + throws StorageQueryException; - KeyValueInfo getLegacyAccessTokenSigningKey_Transaction(TransactionConnection con) throws StorageQueryException; + KeyValueInfo getLegacyAccessTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, + TransactionConnection con) throws StorageQueryException; - KeyValueInfo[] getAccessTokenSigningKeys_Transaction(TransactionConnection con) throws StorageQueryException; + KeyValueInfo[] getAccessTokenSigningKeys_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con) + throws StorageQueryException; - void addAccessTokenSigningKey_Transaction(TransactionConnection con, KeyValueInfo info) + void addAccessTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + KeyValueInfo info) throws StorageQueryException; - KeyValueInfo getRefreshTokenSigningKey_Transaction(TransactionConnection con) throws StorageQueryException; + KeyValueInfo getRefreshTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con) + throws StorageQueryException; - void setRefreshTokenSigningKey_Transaction(TransactionConnection con, KeyValueInfo info) + void setRefreshTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + KeyValueInfo info) throws StorageQueryException; - SessionInfo getSessionInfo_Transaction(TransactionConnection con, String sessionHandle) + SessionInfo getSessionInfo_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String sessionHandle) throws StorageQueryException; - void updateSessionInfo_Transaction(TransactionConnection con, String sessionHandle, String refreshTokenHash2, - long expiry) throws StorageQueryException; + void updateSessionInfo_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String sessionHandle, String refreshTokenHash2, + long expiry) throws StorageQueryException; } From 087403611dee423a61f64a7b361b5ce9b76922a2 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 15 Feb 2023 14:47:23 +0530 Subject: [PATCH 28/93] introduces the concept of appIdentifier vs tenantIdentifier --- .../supertokens/pluginInterface/Storage.java | 3 +- .../authRecipe/AuthRecipeStorage.java | 3 +- .../jwt/sqlstorage/JWTRecipeSQLStorage.java | 6 +- .../multitenancy/AppIdentifier.java | 72 +++++++++++++++++++ .../multitenancy/TenantIdentifier.java | 4 ++ .../session/SessionStorage.java | 3 +- .../session/sqlStorage/SessionSQLStorage.java | 13 ++-- .../useridmapping/UserIdMappingStorage.java | 14 ++-- 8 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 8b80693b..efff6864 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -21,6 +21,7 @@ import io.supertokens.pluginInterface.exceptions.DbInitException; import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import java.util.Set; @@ -69,7 +70,7 @@ public interface Storage { // this function will be used in the createUserIdMapping and deleteUserIdMapping functions to check if the userId is // being used in NonAuth recipes. - boolean isUserIdBeingUsedInNonAuthRecipe(TenantIdentifier tenantIdentifier, String className, String userId) + boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, String className, String userId) throws StorageQueryException; // to be used for testing purposes only. This function will add dummy data to non-auth tables. diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java index 1f30e276..1d805141 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java @@ -20,6 +20,7 @@ import io.supertokens.pluginInterface.RECIPE_ID; import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import javax.annotation.Nonnull; @@ -36,5 +37,5 @@ AuthRecipeUserInfo[] getUsers(TenantIdentifier tenantIdentifier, @Nonnull Intege @Nullable Long timeJoined) throws StorageQueryException; - boolean doesUserIdExist(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + boolean doesUserIdExist(AppIdentifier appIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java index 6c421302..00b4d1c2 100644 --- a/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java @@ -20,14 +20,16 @@ import io.supertokens.pluginInterface.jwt.JWTRecipeStorage; import io.supertokens.pluginInterface.jwt.JWTSigningKeyInfo; import io.supertokens.pluginInterface.jwt.exceptions.DuplicateKeyIdException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import java.util.List; public interface JWTRecipeSQLStorage extends JWTRecipeStorage, SQLStorage { - List getJWTSigningKeys_Transaction(TransactionConnection con) throws StorageQueryException; + List getJWTSigningKeys_Transaction(AppIdentifier appIdentifier, TransactionConnection con) + throws StorageQueryException; - void setJWTSigningKey_Transaction(TransactionConnection con, JWTSigningKeyInfo info) + void setJWTSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con, JWTSigningKeyInfo info) throws StorageQueryException, DuplicateKeyIdException; } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java new file mode 100644 index 00000000..daf9a4aa --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AppIdentifier { + public static final String DEFAULT_APP_ID = "public"; + public static final String DEFAULT_CONNECTION_URI = ""; + + @Nullable + private final String connectionUriDomain; + + @Nullable + private final String appId; + + public AppIdentifier(@Nullable String connectionUriDomain, @Nullable String appId) { + this.connectionUriDomain = connectionUriDomain; + this.appId = appId; + } + + @Nonnull + public String getAppId() { + if (this.appId == null || this.appId.equals("")) { + return DEFAULT_APP_ID; + } + return this.appId.trim().toLowerCase(); + } + + @Nonnull + public String getConnectionUriDomain() { + if (this.connectionUriDomain == null) { + return DEFAULT_CONNECTION_URI; + } + return this.connectionUriDomain.trim().toLowerCase(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof AppIdentifier) { + AppIdentifier otherAppIdentifier = (AppIdentifier) other; + return otherAppIdentifier.getConnectionUriDomain().equals(this.getConnectionUriDomain()) && + otherAppIdentifier.getAppId().equals(this.getAppId()); + } + return false; + } + + @Override + public int hashCode() { + return (this.getConnectionUriDomain() + "|" + + this.getAppId()).hashCode(); + } + + public TenantIdentifier getAsPublicTenantIdentifier() { + return new TenantIdentifier(this.getConnectionUriDomain(), this.getAppId(), null); + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java index f7f754f6..6b1913c0 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -79,4 +79,8 @@ public int hashCode() { return (this.getTenantId() + "|" + this.getConnectionUriDomain() + "|" + this.getAppId()).hashCode(); } + + public AppIdentifier toAppIdentifier() { + return new AppIdentifier(this.getConnectionUriDomain(), this.getAppId()); + } } diff --git a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java index c51da54c..5cb0155b 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java @@ -18,6 +18,7 @@ import com.google.gson.JsonObject; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; @@ -48,5 +49,5 @@ int updateSession(TenantIdentifier tenantIdentifier, String sessionHandle, @Null @Nullable JsonObject jwtPayload) throws StorageQueryException; - void removeAccessTokenSigningKeysBefore(TenantIdentifier tenantIdentifier, long time) throws StorageQueryException; + void removeAccessTokenSigningKeysBefore(AppIdentifier appIdentifier, long time) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java index f98d98b5..de5bd741 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.KeyValueInfo; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.session.SessionInfo; import io.supertokens.pluginInterface.session.SessionStorage; @@ -26,23 +27,23 @@ public interface SessionSQLStorage extends SessionStorage, SQLStorage { - void removeLegacyAccessTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con) + void removeLegacyAccessTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con) throws StorageQueryException; - KeyValueInfo getLegacyAccessTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, + KeyValueInfo getLegacyAccessTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con) throws StorageQueryException; - KeyValueInfo[] getAccessTokenSigningKeys_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con) + KeyValueInfo[] getAccessTokenSigningKeys_Transaction(AppIdentifier appIdentifier, TransactionConnection con) throws StorageQueryException; - void addAccessTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + void addAccessTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con, KeyValueInfo info) throws StorageQueryException; - KeyValueInfo getRefreshTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con) + KeyValueInfo getRefreshTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con) throws StorageQueryException; - void setRefreshTokenSigningKey_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + void setRefreshTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con, KeyValueInfo info) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java index 36ca648f..eab19ae9 100644 --- a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java @@ -18,7 +18,7 @@ import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.useridmapping.exception.UnknownSuperTokensUserIdException; import io.supertokens.pluginInterface.useridmapping.exception.UserIdMappingAlreadyExistsException; @@ -31,26 +31,26 @@ public interface UserIdMappingStorage extends Storage { // whilst these take the full tenantIdentifier as an input, they ignore the tenantId // cause user ID mapping is per app and not per tenant. - void createUserIdMapping(TenantIdentifier tenantIdentifier, String superTokensUserId, String externalUserId, + void createUserIdMapping(AppIdentifier appIdentifier, String superTokensUserId, String externalUserId, @Nullable String externalUserIdInfo) throws StorageQueryException, UnknownSuperTokensUserIdException, UserIdMappingAlreadyExistsException; - boolean deleteUserIdMapping(TenantIdentifier tenantIdentifier, String userId, boolean isSuperTokensUserId) + boolean deleteUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) throws StorageQueryException; - UserIdMapping getUserIdMapping(TenantIdentifier tenantIdentifier, String userId, boolean isSuperTokensUserId) + UserIdMapping getUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) throws StorageQueryException; - UserIdMapping[] getUserIdMapping(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + UserIdMapping[] getUserIdMapping(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - boolean updateOrDeleteExternalUserIdInfo(TenantIdentifier tenantIdentifier, String userId, + boolean updateOrDeleteExternalUserIdInfo(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId, @Nullable String externalUserIdInfo) throws StorageQueryException; // This function will be used to retrieve the userId mapping for a list of userIds. The key of the HashMap will be // superTokensUserId and the value will be the externalUserId. If a mapping does not exist for an input userId, // it will not be in a part of the returned HashMap - HashMap getUserIdMappingForSuperTokensIds(TenantIdentifier tenantIdentifier, + HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier, ArrayList userIds) throws StorageQueryException; } From 01bf511e1969dbe5838dd906566f67524e078673 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 15 Feb 2023 17:59:48 +0530 Subject: [PATCH 29/93] adds a few more functions --- .../pluginInterface/authRecipe/AuthRecipeStorage.java | 2 ++ .../io/supertokens/pluginInterface/session/SessionStorage.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java index 1d805141..abb12f3c 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java @@ -38,4 +38,6 @@ AuthRecipeUserInfo[] getUsers(TenantIdentifier tenantIdentifier, @Nonnull Intege throws StorageQueryException; boolean doesUserIdExist(AppIdentifier appIdentifier, String userId) throws StorageQueryException; + + boolean doesUserIdExist(TenantIdentifier tenantIdentifierIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java index 5cb0155b..f5bb1825 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java @@ -29,7 +29,7 @@ void createNewSession(TenantIdentifier tenantIdentifier, String sessionHandle, S String refreshTokenHash2, JsonObject userDataInDatabase, long expiry, JsonObject userDataInJWT, long createdAtTime) throws StorageQueryException; - void deleteSessionsOfUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + void deleteSessionsOfUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; // return number of rows else throw UnsupportedOperationException int getNumberOfSessions(TenantIdentifier tenantIdentifier) throws StorageQueryException; From 556e3cd8031a5f94c60afab2a1b05012c9fe79b9 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 15 Feb 2023 19:39:23 +0530 Subject: [PATCH 30/93] adds appidentifier to user metadata functions --- .../pluginInterface/usermetadata/UserMetadataStorage.java | 6 +++--- .../usermetadata/sqlStorage/UserMetadataSQLStorage.java | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/usermetadata/UserMetadataStorage.java b/src/main/java/io/supertokens/pluginInterface/usermetadata/UserMetadataStorage.java index 560f77cf..d25b2cfc 100644 --- a/src/main/java/io/supertokens/pluginInterface/usermetadata/UserMetadataStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/usermetadata/UserMetadataStorage.java @@ -17,12 +17,12 @@ package io.supertokens.pluginInterface.usermetadata; import com.google.gson.JsonObject; -import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; public interface UserMetadataStorage extends NonAuthRecipeStorage { - JsonObject getUserMetadata(String userId) throws StorageQueryException; + JsonObject getUserMetadata(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - int deleteUserMetadata(String userId) throws StorageQueryException; + int deleteUserMetadata(AppIdentifier appIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java index 7ffe8f21..6ffa1383 100644 --- a/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java @@ -17,15 +17,17 @@ package io.supertokens.pluginInterface.usermetadata.sqlStorage; import com.google.gson.JsonObject; - import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.usermetadata.UserMetadataStorage; public interface UserMetadataSQLStorage extends UserMetadataStorage, SQLStorage { - JsonObject getUserMetadata_Transaction(TransactionConnection con, String userId) throws StorageQueryException; + JsonObject getUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId) + throws StorageQueryException; - int setUserMetadata_Transaction(TransactionConnection con, String userId, JsonObject metadata) + int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, + JsonObject metadata) throws StorageQueryException; } From 49eba24a91d964dfd2f3b5506a42d3fcbe72ae5b Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 15 Feb 2023 20:43:30 +0530 Subject: [PATCH 31/93] modifes user roles functions to add tenantidentifier and appidentifiers --- .../userroles/UserRolesStorage.java | 24 ++++++++++--------- .../sqlStorage/UserRolesSQLStorage.java | 23 ++++++++++++------ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java b/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java index ddf91633..3b816e44 100644 --- a/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java @@ -16,9 +16,9 @@ package io.supertokens.pluginInterface.userroles; -import com.google.gson.JsonObject; -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.nonAuthRecipe.NonAuthRecipeStorage; import io.supertokens.pluginInterface.userroles.exception.DuplicateUserRoleMappingException; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; @@ -26,30 +26,32 @@ public interface UserRolesStorage extends NonAuthRecipeStorage { // associate a userId with a role that exists - void addRoleToUser(String userId, String role) + void addRoleToUser(TenantIdentifier tenantIdentifier, String userId, String role) throws StorageQueryException, UnknownRoleException, DuplicateUserRoleMappingException; // get all roles associated with the input userId - String[] getRolesForUser(String userId) throws StorageQueryException; + String[] getRolesForUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; // get all users associated with the input role - String[] getUsersForRole(String role) throws StorageQueryException; + String[] getUsersForRole(TenantIdentifier tenantIdentifier, String role) throws StorageQueryException; // get permissions associated with the input role - String[] getPermissionsForRole(String role) throws StorageQueryException; + String[] getPermissionsForRole(AppIdentifier appIdentifier, String role) throws StorageQueryException; // get roles associated with the input permission - String[] getRolesThatHavePermission(String permission) throws StorageQueryException; + String[] getRolesThatHavePermission(AppIdentifier appIdentifier, String permission) throws StorageQueryException; // delete a role - boolean deleteRole(String role) throws StorageQueryException; + boolean deleteRole(AppIdentifier appIdentifier, String role) throws StorageQueryException; // get all created roles - String[] getRoles() throws StorageQueryException; + String[] getRoles(AppIdentifier appIdentifier) throws StorageQueryException; // check if input roles exists - boolean doesRoleExist(String role) throws StorageQueryException; + boolean doesRoleExist(AppIdentifier appIdentifier, String role) throws StorageQueryException; // delete all roles for the input userId - int deleteAllRolesForUser(String userId) throws StorageQueryException; + int deleteAllRolesForUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + + void deleteAllRolesForUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java index 0a51a071..18c78f25 100644 --- a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java @@ -17,6 +17,8 @@ package io.supertokens.pluginInterface.userroles.sqlStorage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.userroles.UserRolesStorage; @@ -25,24 +27,31 @@ public interface UserRolesSQLStorage extends UserRolesStorage, SQLStorage { // delete role associated with the input userId from the input roles - boolean deleteRoleForUser_Transaction(TransactionConnection con, String userId, String role) + boolean deleteRoleForUser_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId, + String role) throws StorageQueryException; - // create a new role if it doesnt exist - boolean createNewRoleOrDoNothingIfExists_Transaction(TransactionConnection con, String role) + // create a new role if it doesnt exist. The reason this has tenantIdentifier is that it also adds the + // tenantId <-> role mapping + boolean createNewRoleOrDoNothingIfExists_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String role) throws StorageQueryException; // associate a permission with a role - void addPermissionToRoleOrDoNothingIfExists_Transaction(TransactionConnection con, String role, String permission) + void addPermissionToRoleOrDoNothingIfExists_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String role, String permission) throws StorageQueryException, UnknownRoleException; // delete a permission associated with the input role - boolean deletePermissionForRole_Transaction(TransactionConnection con, String role, String permission) + boolean deletePermissionForRole_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role, + String permission) throws StorageQueryException; // delete all permissions associated with the input role - int deleteAllPermissionsForRole_Transaction(TransactionConnection con, String role) throws StorageQueryException; + int deleteAllPermissionsForRole_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) + throws StorageQueryException; // check if a role exists - boolean doesRoleExist_Transaction(TransactionConnection con, String role) throws StorageQueryException; + boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) + throws StorageQueryException; } From 3b4a43fcbb0e7237c96ba6c7e25d2b5e9e0cceea Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 16 Feb 2023 22:41:50 +0530 Subject: [PATCH 32/93] changes to emailpassword functions --- .../emailpassword/EmailPasswordStorage.java | 20 ++++++++++++---- .../sqlStorage/EmailPasswordSQLStorage.java | 24 +++++++++++++++---- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index ec19a9bc..28829473 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -22,30 +22,40 @@ import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; public interface EmailPasswordStorage extends AuthRecipeStorage { + // we pass tenantIdentifier here cause this also adds to the userId <-> tenantId mapping void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException; - void deleteEmailPasswordUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + // this deletion of a user is app wide since the same user ID can be shared across tenants + void deleteEmailPasswordUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - UserInfo getUserInfoUsingId(TenantIdentifier tenantIdentifier, String id) throws StorageQueryException; + // Here we pass TenantIdentifier and not AppIdentifier cause the query will yield an exact row, + // as opposed to AppIdentifier in which it would give us an array of users and we would then have + // to pick the 0th element from it. + UserInfo getUserInfoUsingId(TenantIdentifier appIdentifier, String id) throws StorageQueryException; + // Here we pass in TenantIdentifier cause the same email can be shared across tenants, but yield different + // user IDs UserInfo getUserInfoUsingEmail(TenantIdentifier tenantIdentifier, String email) throws StorageQueryException; - void addPasswordResetToken(TenantIdentifier tenantIdentifier, PasswordResetTokenInfo passwordResetTokenInfo) + // password reset stuff is app wide cause changing the password for a user affects all the tenants + // across which it's shared. + void addPasswordResetToken(AppIdentifier appIdentifier, PasswordResetTokenInfo passwordResetTokenInfo) throws StorageQueryException, UnknownUserIdException, DuplicatePasswordResetTokenException; - PasswordResetTokenInfo getPasswordResetTokenInfo(TenantIdentifier tenantIdentifier, String token) + PasswordResetTokenInfo getPasswordResetTokenInfo(AppIdentifier appIdentifier, String token) throws StorageQueryException; // we purposely do not add TenantIdentifier to this query cause // this is called from a cronjob that runs per user pool ID void deleteExpiredPasswordResetTokens() throws StorageQueryException; - PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser(TenantIdentifier tenantIdentifier, String userId) + PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java index fd926299..5775b76d 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java @@ -21,22 +21,36 @@ import io.supertokens.pluginInterface.emailpassword.UserInfo; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface EmailPasswordSQLStorage extends EmailPasswordStorage, SQLStorage { - PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser_Transaction(TransactionConnection con, String userId) + // all password reset related stuff is app wide cause the same user ID can be shared across tenants, + // and updating / resetting a user's password should apply to all those tenants. + + PasswordResetTokenInfo[] getAllPasswordResetTokenInfoForUser_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, String userId) throws StorageQueryException; - void deleteAllPasswordResetTokensForUser_Transaction(TransactionConnection con, String userId) + void deleteAllPasswordResetTokensForUser_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String userId) throws StorageQueryException; - void updateUsersPassword_Transaction(TransactionConnection con, String userId, String newPassword) + void updateUsersPassword_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, + String newPassword) throws StorageQueryException; - void updateUsersEmail_Transaction(TransactionConnection conn, String userId, String email) + void updateUsersEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection conn, String userId, + String email) throws StorageQueryException, DuplicateEmailException; - UserInfo getUserInfoUsingId_Transaction(TransactionConnection con, String userId) throws StorageQueryException; + // this is tenant specific, but doesn't really need to be since the same user ID should yield the + // same userInfo object across tenants. The only reason this takes a TenantIdentifier is cause + // then in the query we get an exact result as opposed to an array of users (and then we have to pick the 0th + // element) + UserInfo getUserInfoUsingId_Transaction(TenantIdentifier appIdentifier, TransactionConnection con, String userId) + throws StorageQueryException; } From 6c201e9a23095688704b9765f8d753f3f189eafc Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Fri, 17 Feb 2023 12:39:43 +0530 Subject: [PATCH 33/93] changes to a few functions --- .../emailpassword/EmailPasswordStorage.java | 7 ++----- .../emailpassword/sqlStorage/EmailPasswordSQLStorage.java | 7 +------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index 28829473..eb535fed 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -33,11 +33,8 @@ void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) // this deletion of a user is app wide since the same user ID can be shared across tenants void deleteEmailPasswordUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - - // Here we pass TenantIdentifier and not AppIdentifier cause the query will yield an exact row, - // as opposed to AppIdentifier in which it would give us an array of users and we would then have - // to pick the 0th element from it. - UserInfo getUserInfoUsingId(TenantIdentifier appIdentifier, String id) throws StorageQueryException; + + UserInfo getUserInfoUsingId(AppIdentifier appIdentifier, String id) throws StorageQueryException; // Here we pass in TenantIdentifier cause the same email can be shared across tenants, but yield different // user IDs diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java index 5775b76d..78b5ea68 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java @@ -22,7 +22,6 @@ import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; -import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; @@ -47,10 +46,6 @@ void updateUsersEmail_Transaction(AppIdentifier appIdentifier, TransactionConnec String email) throws StorageQueryException, DuplicateEmailException; - // this is tenant specific, but doesn't really need to be since the same user ID should yield the - // same userInfo object across tenants. The only reason this takes a TenantIdentifier is cause - // then in the query we get an exact result as opposed to an array of users (and then we have to pick the 0th - // element) - UserInfo getUserInfoUsingId_Transaction(TenantIdentifier appIdentifier, TransactionConnection con, String userId) + UserInfo getUserInfoUsingId_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId) throws StorageQueryException; } From d0071ed46c406b7ca42a4b51e6ec84d5d1f15478 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Fri, 17 Feb 2023 14:08:25 +0530 Subject: [PATCH 34/93] adds appidentifier to email verfication --- .../EmailVerificationStorage.java | 18 ++++++++++-------- .../EmailVerificationSQLStorage.java | 15 ++++++++++----- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java index c9df0b26..3e2d6f10 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java @@ -16,29 +16,31 @@ package io.supertokens.pluginInterface.emailverification; -import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.emailverification.exception.DuplicateEmailVerificationTokenException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; public interface EmailVerificationStorage extends NonAuthRecipeStorage { - void addEmailVerificationToken(EmailVerificationTokenInfo emailVerificationInfo) + void addEmailVerificationToken(AppIdentifier appIdentifier, EmailVerificationTokenInfo emailVerificationInfo) throws StorageQueryException, DuplicateEmailVerificationTokenException; - EmailVerificationTokenInfo getEmailVerificationTokenInfo(String token) throws StorageQueryException; + EmailVerificationTokenInfo getEmailVerificationTokenInfo(AppIdentifier appIdentifier, String token) + throws StorageQueryException; - void deleteEmailVerificationUserInfo(String userId) throws StorageQueryException; + void deleteEmailVerificationUserInfo(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - void revokeAllTokens(String userId, String email) throws StorageQueryException; + void revokeAllTokens(AppIdentifier appIdentifier, String userId, String email) throws StorageQueryException; - void unverifyEmail(String userId, String email) throws StorageQueryException; + void unverifyEmail(AppIdentifier appIdentifier, String userId, String email) throws StorageQueryException; void deleteExpiredEmailVerificationTokens() throws StorageQueryException; - EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser(String userId, String email) + EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser(AppIdentifier appIdentifier, String userId, + String email) throws StorageQueryException; - boolean isEmailVerified(String userId, String email) throws StorageQueryException; + boolean isEmailVerified(AppIdentifier appIdentifier, String userId, String email) throws StorageQueryException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java index 7c21f312..b6739880 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java @@ -19,18 +19,23 @@ import io.supertokens.pluginInterface.emailverification.EmailVerificationStorage; import io.supertokens.pluginInterface.emailverification.EmailVerificationTokenInfo; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface EmailVerificationSQLStorage extends EmailVerificationStorage, SQLStorage { - EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser_Transaction(TransactionConnection con, - String userId, String email) throws StorageQueryException; + EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, + String userId, String email) + throws StorageQueryException; - void deleteAllEmailVerificationTokensForUser_Transaction(TransactionConnection con, String userId, String email) + void deleteAllEmailVerificationTokensForUser_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String userId, String email) throws StorageQueryException; - void updateIsEmailVerified_Transaction(TransactionConnection con, String userId, String email, - boolean isEmailVerified) throws StorageQueryException; + void updateIsEmailVerified_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, + String email, + boolean isEmailVerified) throws StorageQueryException; } From d08a59b2631217ce93534652967db253dd4b62d2 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Fri, 17 Feb 2023 17:14:13 +0530 Subject: [PATCH 35/93] adds tenant identifier to third party --- .../thirdparty/ThirdPartyStorage.java | 24 +++++++------------ .../sqlStorage/ThirdPartySQLStorage.java | 9 ++++--- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java index 0b820046..950b744c 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java @@ -18,6 +18,8 @@ import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException; import io.supertokens.pluginInterface.thirdparty.exception.DuplicateUserIdException; @@ -25,24 +27,16 @@ public interface ThirdPartyStorage extends AuthRecipeStorage { - void signUp(UserInfo userInfo) + void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) throws StorageQueryException, DuplicateUserIdException, DuplicateThirdPartyUserException; - void deleteThirdPartyUser(String userId) throws StorageQueryException; + void deleteThirdPartyUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - UserInfo getThirdPartyUserInfoUsingId(String thirdPartyId, String thirdPartyUserId) throws StorageQueryException; + UserInfo getThirdPartyUserInfoUsingId(TenantIdentifier tenantIdentifier, String thirdPartyId, + String thirdPartyUserId) throws StorageQueryException; - UserInfo getThirdPartyUserInfoUsingId(String userId) throws StorageQueryException; + UserInfo getThirdPartyUserInfoUsingId(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - @Deprecated - UserInfo[] getThirdPartyUsers(@Nonnull String userId, @Nonnull Long timeJoined, @Nonnull Integer limit, - @Nonnull String timeJoinedOrder) throws StorageQueryException; - - @Deprecated - UserInfo[] getThirdPartyUsers(@Nonnull Integer limit, @Nonnull String timeJoinedOrder) throws StorageQueryException; - - @Deprecated - long getThirdPartyUsersCount() throws StorageQueryException; - - UserInfo[] getThirdPartyUsersByEmail(@Nonnull String email) throws StorageQueryException; + UserInfo[] getThirdPartyUsersByEmail(TenantIdentifier tenantIdentifier, @Nonnull String email) + throws StorageQueryException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java index e0fdd4ba..0d23a29f 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java @@ -17,6 +17,7 @@ package io.supertokens.pluginInterface.thirdparty.sqlStorage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.thirdparty.ThirdPartyStorage; @@ -24,9 +25,11 @@ public interface ThirdPartySQLStorage extends ThirdPartyStorage, SQLStorage { - UserInfo getUserInfoUsingId_Transaction(TransactionConnection con, String thirdPartyId, String thirdPartyUserId) + UserInfo getUserInfoUsingId_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String thirdPartyId, String thirdPartyUserId) throws StorageQueryException; - void updateUserEmail_Transaction(TransactionConnection con, String thirdPartyId, String thirdPartyUserId, - String newEmail) throws StorageQueryException; + void updateUserEmail_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String thirdPartyId, + String thirdPartyUserId, + String newEmail) throws StorageQueryException; } From ac6c0994c3525c4407dce6f8300cce1d5becee42 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Fri, 17 Feb 2023 18:57:19 +0530 Subject: [PATCH 36/93] adds tenantidentifier to passwordless --- .../passwordless/PasswordlessStorage.java | 45 +++++++++------- .../sqlStorage/PasswordlessSQLStorage.java | 52 ++++++++++++++----- 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java index 19e6da46..1f53bf40 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java @@ -20,45 +20,50 @@ import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.passwordless.exception.DuplicateCodeIdException; -import io.supertokens.pluginInterface.passwordless.exception.DuplicateDeviceIdHashException; -import io.supertokens.pluginInterface.passwordless.exception.DuplicateLinkCodeHashException; -import io.supertokens.pluginInterface.passwordless.exception.DuplicatePhoneNumberException; -import io.supertokens.pluginInterface.passwordless.exception.UnknownDeviceIdHash; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.passwordless.exception.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; public interface PasswordlessStorage extends AuthRecipeStorage { - void createDeviceWithCode(@Nullable String email, @Nullable String phoneNumber, @Nonnull String linkCodeSalt, - PasswordlessCode code) throws StorageQueryException, DuplicateDeviceIdHashException, + void createDeviceWithCode(TenantIdentifier tenantIdentifier, @Nullable String email, @Nullable String phoneNumber, + @Nonnull String linkCodeSalt, + PasswordlessCode code) throws StorageQueryException, DuplicateDeviceIdHashException, DuplicateCodeIdException, DuplicateLinkCodeHashException; - void createCode(PasswordlessCode code) + void createCode(TenantIdentifier tenantIdentifier, PasswordlessCode code) throws StorageQueryException, UnknownDeviceIdHash, DuplicateCodeIdException, DuplicateLinkCodeHashException; - void createUser(UserInfo user) throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, + void createUser(TenantIdentifier tenantIdentifier, UserInfo user) + throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, DuplicateUserIdException; - void deletePasswordlessUser(String userId) throws StorageQueryException; + void deletePasswordlessUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - PasswordlessDevice getDevice(String deviceIdHash) throws StorageQueryException; + PasswordlessDevice getDevice(TenantIdentifier tenantIdentifier, String deviceIdHash) throws StorageQueryException; - PasswordlessDevice[] getDevicesByEmail(@Nonnull String email) throws StorageQueryException; + PasswordlessDevice[] getDevicesByEmail(TenantIdentifier tenantIdentifier, @Nonnull String email) + throws StorageQueryException; - PasswordlessDevice[] getDevicesByPhoneNumber(@Nonnull String phoneNumber) throws StorageQueryException; + PasswordlessDevice[] getDevicesByPhoneNumber(TenantIdentifier tenantIdentifier, @Nonnull String phoneNumber) + throws StorageQueryException; - PasswordlessCode[] getCodesOfDevice(String deviceIdHash) throws StorageQueryException; + PasswordlessCode[] getCodesOfDevice(TenantIdentifier tenantIdentifier, String deviceIdHash) + throws StorageQueryException; - PasswordlessCode[] getCodesBefore(long time) throws StorageQueryException; + PasswordlessCode[] getCodesBefore(TenantIdentifier tenantIdentifier, long time) throws StorageQueryException; - PasswordlessCode getCode(String codeId) throws StorageQueryException; + PasswordlessCode getCode(TenantIdentifier tenantIdentifier, String codeId) throws StorageQueryException; - PasswordlessCode getCodeByLinkCodeHash(String linkCode) throws StorageQueryException; + PasswordlessCode getCodeByLinkCodeHash(TenantIdentifier tenantIdentifier, String linkCode) + throws StorageQueryException; - UserInfo getUserById(String userId) throws StorageQueryException; + UserInfo getUserById(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - UserInfo getUserByEmail(@Nonnull String email) throws StorageQueryException; + UserInfo getUserByEmail(TenantIdentifier tenantIdentifier, @Nonnull String email) throws StorageQueryException; - UserInfo getUserByPhoneNumber(@Nonnull String phoneNumber) throws StorageQueryException; + UserInfo getUserByPhoneNumber(TenantIdentifier tenantIdentifier, @Nonnull String phoneNumber) + throws StorageQueryException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java index b703a075..ef47754f 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java @@ -16,12 +16,11 @@ package io.supertokens.pluginInterface.passwordless.sqlStorage; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; import io.supertokens.pluginInterface.passwordless.PasswordlessStorage; @@ -29,32 +28,57 @@ import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public interface PasswordlessSQLStorage extends PasswordlessStorage, SQLStorage { - PasswordlessDevice getDevice_Transaction(TransactionConnection con, String deviceIdHash) + PasswordlessDevice getDevice_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String deviceIdHash) + throws StorageQueryException; + + void incrementDeviceFailedAttemptCount_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String deviceIdHash) throws StorageQueryException; - void incrementDeviceFailedAttemptCount_Transaction(TransactionConnection con, String deviceIdHash) + PasswordlessCode[] getCodesOfDevice_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String deviceIdHash) throws StorageQueryException; - PasswordlessCode[] getCodesOfDevice_Transaction(TransactionConnection con, String deviceIdHash) + void deleteDevice_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String deviceIdHash) throws StorageQueryException; - void deleteDevice_Transaction(TransactionConnection con, String deviceIdHash) throws StorageQueryException; + // we have deleteDevicesBy* for tenantIdentifier and for appIdentifier cause the tenantIdentifier version is + // used when trying to log into one specific tenant. But if the user's detail is updated, then this + // would affect all the tenants that share that userId. + + void deleteDevicesByPhoneNumber_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String phoneNumber) + throws StorageQueryException; - void deleteDevicesByPhoneNumber_Transaction(TransactionConnection con, String phoneNumber) + void deleteDevicesByEmail_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String email) throws StorageQueryException; - void deleteDevicesByEmail_Transaction(TransactionConnection con, String email) throws StorageQueryException; + void deleteDevicesByPhoneNumber_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String phoneNumber, String userId) + throws StorageQueryException; - PasswordlessCode getCodeByLinkCodeHash_Transaction(TransactionConnection con, String linkCodeHash) + void deleteDevicesByEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String email, + String userId) throws StorageQueryException; - void deleteCode_Transaction(TransactionConnection con, String codeId) throws StorageQueryException; + PasswordlessCode getCodeByLinkCodeHash_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + String linkCodeHash) + throws StorageQueryException; + + void deleteCode_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String codeId) + throws StorageQueryException; - void updateUserEmail_Transaction(TransactionConnection con, @Nonnull String userId, @Nullable String email) + void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, @Nonnull String userId, + @Nullable String email) throws StorageQueryException, UnknownUserIdException, DuplicateEmailException; - void updateUserPhoneNumber_Transaction(TransactionConnection con, @Nonnull String userId, - @Nullable String phoneNumber) + void updateUserPhoneNumber_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + @Nonnull String userId, + @Nullable String phoneNumber) throws StorageQueryException, UnknownUserIdException, DuplicatePhoneNumberException; } From 976362fa2cabcd0aac9a2148f52a33fc123baae4 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Mon, 20 Feb 2023 14:06:46 +0530 Subject: [PATCH 37/93] function name changes --- .../pluginInterface/multitenancy/MultitenancyStorage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index a73ff17f..980b84ec 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -48,8 +48,8 @@ void addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws void addRoleToTenant(TenantIdentifier tenantIdentifier, String role) throws TenantOrAppNotFoundException, UnknownRoleException; - void markAppIdAsDeleted(String appId) throws TenantOrAppNotFoundException; + void deleteAppId(String appId) throws TenantOrAppNotFoundException; - void markConnectionUriDomainAsDeleted(String connectionUriDomain) throws TenantOrAppNotFoundException; + void deleteConnectionUriDomain(String connectionUriDomain) throws TenantOrAppNotFoundException; } \ No newline at end of file From a36b7f671c9276d560f63972f5c9111ff28d49d6 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 24 Feb 2023 12:04:20 +0530 Subject: [PATCH 38/93] fix: changes for multi-tenancy impl (#55) * fix: pr comments * fix: pr comments --- .../multitenancy/MultitenancyStorage.java | 9 +++--- .../multitenancy/ThirdPartyConfig.java | 28 +++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index 980b84ec..340fdd55 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -18,21 +18,22 @@ import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; +import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; public interface MultitenancyStorage extends Storage { - void createTenant(TenantConfig config) throws DuplicateTenantException; + void createTenant(TenantConfig config) throws DuplicateTenantException, StorageQueryException; // this adds tenantId to the target user pool - void addTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws DuplicateTenantException; + void addTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws DuplicateTenantException, StorageQueryException; // this also deletes all tenant info from all tables. void deleteTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; - void overwriteTenantConfig(TenantConfig config) throws TenantOrAppNotFoundException; + void overwriteTenantConfig(TenantConfig config) throws TenantOrAppNotFoundException, StorageQueryException; void deleteTenant(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; @@ -40,7 +41,7 @@ public interface MultitenancyStorage extends Storage { void deleteConnectionUriDomainMapping(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; - TenantConfig[] getAllTenants(); + TenantConfig[] getAllTenants() throws StorageQueryException; void addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws TenantOrAppNotFoundException, UnknownUserIdException; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index 1237ac38..269284a1 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -43,7 +43,7 @@ public static class Provider { public final String name; @Nullable - public final ProviderClients[] clients; + public final ProviderClient[] clients; @Nullable public final String authorizationEndpoint; @@ -77,7 +77,7 @@ public static class Provider { @Nullable public final UserInfoMap userInfoMap; - public Provider(@Nonnull String thirdPartyId, @Nonnull String name, @Nullable ProviderClients[] clients, + public Provider(@Nonnull String thirdPartyId, @Nonnull String name, @Nullable ProviderClient[] clients, @Nullable String authorizationEndpoint, @Nullable JsonObject authorizationEndpointQueryParams, @Nullable String tokenEndpoint, @Nullable JsonObject tokenEndpointBodyParams, @@ -125,7 +125,7 @@ public boolean equals(Object other) { } } - public static class ProviderClients { + public static class ProviderClient { @Nullable public final String clientType; @@ -144,9 +144,9 @@ public static class ProviderClients { @Nullable public final JsonObject additionalConfig; - public ProviderClients(@Nullable String clientType, @Nonnull String clientId, @Nullable String clientSecret, - @Nullable String[] scope, - boolean forcePKCE, @Nullable JsonObject additionalConfig) { + public ProviderClient(@Nullable String clientType, @Nonnull String clientId, @Nullable String clientSecret, + @Nullable String[] scope, + boolean forcePKCE, @Nullable JsonObject additionalConfig) { this.clientType = clientType; this.clientId = clientId; this.clientSecret = clientSecret; @@ -157,14 +157,14 @@ public ProviderClients(@Nullable String clientType, @Nonnull String clientId, @N @Override public boolean equals(Object other) { - if (other instanceof ProviderClients) { - ProviderClients otherProviderClients = (ProviderClients) other; - return Objects.equals(otherProviderClients.clientType, this.clientType) && - otherProviderClients.clientId.equals(this.clientId) && - Objects.equals(otherProviderClients.clientSecret, this.clientSecret) && - Arrays.equals(otherProviderClients.scope, this.scope) && - otherProviderClients.forcePKCE == this.forcePKCE && - Objects.equals(otherProviderClients.additionalConfig, this.additionalConfig); + if (other instanceof ProviderClient) { + ProviderClient otherProviderClient = (ProviderClient) other; + return Objects.equals(otherProviderClient.clientType, this.clientType) && + otherProviderClient.clientId.equals(this.clientId) && + Objects.equals(otherProviderClient.clientSecret, this.clientSecret) && + Arrays.equals(otherProviderClient.scope, this.scope) && + otherProviderClient.forcePKCE == this.forcePKCE && + Objects.equals(otherProviderClient.additionalConfig, this.additionalConfig); } return false; } From 2018b3da8c7e32f76d755fa9e5663b7c4464eef7 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 28 Feb 2023 14:18:59 +0530 Subject: [PATCH 39/93] fix: new exceptions (#56) * fix: pr comments * fix: pr comments * fix: added few more exceptions * fix: pr comments * fix: userInfoMap consistent with db * fix: non null --- .../multitenancy/MultitenancyStorage.java | 11 +++++++--- .../multitenancy/ThirdPartyConfig.java | 12 +++++------ .../DuplicateClientTypeException.java | 20 +++++++++++++++++++ .../DuplicateThirdPartyIdException.java | 20 +++++++++++++++++++ 4 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateClientTypeException.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateThirdPartyIdException.java diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index 340fdd55..25953fb3 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -19,21 +19,26 @@ import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateClientTypeException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; +import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; public interface MultitenancyStorage extends Storage { - void createTenant(TenantConfig config) throws DuplicateTenantException, StorageQueryException; + void createTenant(TenantConfig config) throws DuplicateTenantException, DuplicateThirdPartyIdException, + DuplicateClientTypeException, StorageQueryException; // this adds tenantId to the target user pool - void addTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws DuplicateTenantException, StorageQueryException; + void addTenantIdInUserPool(TenantIdentifier tenantIdentifier) + throws DuplicateTenantException, StorageQueryException; // this also deletes all tenant info from all tables. void deleteTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; - void overwriteTenantConfig(TenantConfig config) throws TenantOrAppNotFoundException, StorageQueryException; + void overwriteTenantConfig(TenantConfig config) throws TenantOrAppNotFoundException, + DuplicateThirdPartyIdException, DuplicateClientTypeException, StorageQueryException; void deleteTenant(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index 269284a1..7b372c48 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -74,7 +74,7 @@ public static class Provider { public final boolean requireEmail; - @Nullable + @Nonnull public final UserInfoMap userInfoMap; public Provider(@Nonnull String thirdPartyId, @Nonnull String name, @Nullable ProviderClient[] clients, @@ -98,7 +98,7 @@ public Provider(@Nonnull String thirdPartyId, @Nonnull String name, @Nullable Pr this.jwksURI = jwksURI; this.oidcDiscoveryEndpoint = oidcDiscoveryEndpoint; this.requireEmail = requireEmail; - this.userInfoMap = userInfoMap; + this.userInfoMap = userInfoMap == null ? new UserInfoMap(null, null) : userInfoMap; } @Override @@ -171,16 +171,16 @@ public boolean equals(Object other) { } public static class UserInfoMap { - @Nullable + @Nonnull public UserInfoMapKeyValue fromIdTokenPayload; - @Nullable + @Nonnull public UserInfoMapKeyValue fromUserInfoAPI; public UserInfoMap(@Nullable UserInfoMapKeyValue fromIdTokenPayload, @Nullable UserInfoMapKeyValue fromUserInfoAPI) { - this.fromIdTokenPayload = fromIdTokenPayload; - this.fromUserInfoAPI = fromUserInfoAPI; + this.fromIdTokenPayload = fromIdTokenPayload == null ? new UserInfoMapKeyValue(null, null, null) : fromIdTokenPayload; + this.fromUserInfoAPI = fromUserInfoAPI == null ? new UserInfoMapKeyValue(null, null, null) : fromUserInfoAPI; } @Override diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateClientTypeException.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateClientTypeException.java new file mode 100644 index 00000000..733ef82e --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateClientTypeException.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy.exceptions; + +public class DuplicateClientTypeException extends Exception { +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateThirdPartyIdException.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateThirdPartyIdException.java new file mode 100644 index 00000000..416c60e3 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/DuplicateThirdPartyIdException.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy.exceptions; + +public class DuplicateThirdPartyIdException extends Exception { +} From dcd6e9639c63c7ebd4dfe2d609296c0c1fff6af4 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 2 Mar 2023 22:39:39 +0530 Subject: [PATCH 40/93] fix: changes for random test (#57) * fix: pr comments * fix: pr comments * fix: added few more exceptions * fix: pr comments * fix: userInfoMap consistent with db * fix: non null * fix: update for random test --- .../multitenancy/TenantConfig.java | 4 ++-- .../multitenancy/ThirdPartyConfig.java | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index 0b35acbb..ba456bca 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -30,10 +30,10 @@ public class TenantConfig { public final EmailPasswordConfig emailPasswordConfig; @Nonnull - public final PasswordlessConfig passwordlessConfig; + public final ThirdPartyConfig thirdPartyConfig; @Nonnull - public final ThirdPartyConfig thirdPartyConfig; + public final PasswordlessConfig passwordlessConfig; @Nonnull public final JsonObject coreConfig; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index 7b372c48..b0cf54e2 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -17,6 +17,7 @@ package io.supertokens.pluginInterface.multitenancy; import com.google.gson.JsonObject; +import io.supertokens.pluginInterface.exceptions.InvalidConfigException; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -42,7 +43,7 @@ public static class Provider { @Nonnull public final String name; - @Nullable + @Nonnull public final ProviderClient[] clients; @Nullable @@ -72,7 +73,8 @@ public static class Provider { @Nullable public final String oidcDiscoveryEndpoint; - public final boolean requireEmail; + @Nullable + public final Boolean requireEmail; @Nonnull public final UserInfoMap userInfoMap; @@ -83,11 +85,11 @@ public Provider(@Nonnull String thirdPartyId, @Nonnull String name, @Nullable Pr @Nullable JsonObject tokenEndpointBodyParams, @Nullable String userInfoEndpoint, @Nullable JsonObject userInfoEndpointQueryParams, @Nullable JsonObject userInfoEndpointHeaders, - @Nullable String jwksURI, @Nullable String oidcDiscoveryEndpoint, boolean requireEmail, + @Nullable String jwksURI, @Nullable String oidcDiscoveryEndpoint, @Nullable Boolean requireEmail, @Nullable UserInfoMap userInfoMap) { this.thirdPartyId = thirdPartyId; this.name = name; - this.clients = clients; + this.clients = clients == null ? new ProviderClient[0] : clients; this.authorizationEndpoint = authorizationEndpoint; this.authorizationEndpointQueryParams = authorizationEndpointQueryParams; this.tokenEndpoint = tokenEndpoint; @@ -139,14 +141,15 @@ public static class ProviderClient { @Nullable public final String[] scope; - public final boolean forcePKCE; + @Nullable + public final Boolean forcePKCE; @Nullable public final JsonObject additionalConfig; public ProviderClient(@Nullable String clientType, @Nonnull String clientId, @Nullable String clientSecret, @Nullable String[] scope, - boolean forcePKCE, @Nullable JsonObject additionalConfig) { + @Nullable Boolean forcePKCE, @Nullable JsonObject additionalConfig) { this.clientType = clientType; this.clientId = clientId; this.clientSecret = clientSecret; From 9b9e2284bce296b2bbf52bfea346aea53d2b74cb Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Sun, 5 Mar 2023 13:02:52 +0530 Subject: [PATCH 41/93] makes dashboard interface per app --- .../dashboard/DashboardStorage.java | 28 +++++++++++-------- .../sqlStorage/DashboardSQLStorage.java | 11 ++++++-- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java b/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java index f24028b8..41e8061a 100644 --- a/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java @@ -1,30 +1,36 @@ package io.supertokens.pluginInterface.dashboard; import io.supertokens.pluginInterface.Storage; -import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.dashboard.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.dashboard.exceptions.DuplicateUserIdException; import io.supertokens.pluginInterface.dashboard.exceptions.UserIdNotFoundException; +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; public interface DashboardStorage extends Storage { - void createNewDashboardUser(DashboardUser userInfo) throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException; + void createNewDashboardUser(AppIdentifier appIdentifier, DashboardUser userInfo) + throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException; + + DashboardUser getDashboardUserByEmail(AppIdentifier appIdentifier, String email) throws StorageQueryException; - DashboardUser getDashboardUserByEmail(String email) throws StorageQueryException; + DashboardUser getDashboardUserByUserId(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - DashboardUser getDashboardUserByUserId(String userId) throws StorageQueryException; + DashboardUser[] getAllDashboardUsers(AppIdentifier appIdentifier) throws StorageQueryException; - DashboardUser[] getAllDashboardUsers() throws StorageQueryException; + boolean deleteDashboardUserWithUserId(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - boolean deleteDashboardUserWithUserId(String userId) throws StorageQueryException; - - void createNewDashboardUserSession(String userId, String sessionId, long timeCreated, long expiry) throws StorageQueryException, UserIdNotFoundException; + void createNewDashboardUserSession(AppIdentifier appIdentifier, String userId, String sessionId, long timeCreated, + long expiry) throws StorageQueryException, UserIdNotFoundException; - DashboardSessionInfo[] getAllSessionsForUserId(String userId) throws StorageQueryException; + DashboardSessionInfo[] getAllSessionsForUserId(AppIdentifier appIdentifier, String userId) + throws StorageQueryException; - DashboardSessionInfo getSessionInfoWithSessionId(String sessionId) throws StorageQueryException; + DashboardSessionInfo getSessionInfoWithSessionId(AppIdentifier appIdentifier, String sessionId) + throws StorageQueryException; - boolean revokeSessionWithSessionId(String sessionId) throws StorageQueryException; + boolean revokeSessionWithSessionId(AppIdentifier appIdentifier, String sessionId) throws StorageQueryException; + // this function removes based on expired time, so we can use this to globally remove from a particular db. void revokeExpiredSessions() throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/dashboard/sqlStorage/DashboardSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/dashboard/sqlStorage/DashboardSQLStorage.java index 1237ea15..2790b590 100644 --- a/src/main/java/io/supertokens/pluginInterface/dashboard/sqlStorage/DashboardSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/dashboard/sqlStorage/DashboardSQLStorage.java @@ -4,13 +4,18 @@ import io.supertokens.pluginInterface.dashboard.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.dashboard.exceptions.UserIdNotFoundException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface DashboardSQLStorage extends DashboardStorage, SQLStorage { - void updateDashboardUsersEmailWithUserId_Transaction(TransactionConnection con, String userId, String newEmail) throws StorageQueryException, DuplicateEmailException, UserIdNotFoundException; + void updateDashboardUsersEmailWithUserId_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String userId, String newEmail) + throws StorageQueryException, DuplicateEmailException, UserIdNotFoundException; + + void updateDashboardUsersPasswordWithUserId_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String userId, String newPassword) + throws StorageQueryException, UserIdNotFoundException; - void updateDashboardUsersPasswordWithUserId_Transaction(TransactionConnection con, String userId, String newPassword) throws StorageQueryException, UserIdNotFoundException; - } \ No newline at end of file From 0a1ee4b76fc8e52b22bbed03cedb4140893c0373 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 24 Mar 2023 14:40:03 +0530 Subject: [PATCH 42/93] fix: storage in AppIdentifier and TenantIdentifier (#61) * fix: storage in AppIdentifier and TenantIdentifier * fix: pr comments * fix: changes for delete user * fix: changes for delete user * fix: revert * fix: updated exception msg * fix: pr comment * fix: adding tenant or app not found exceptions * fix: adding tenant or app not found exceptions --- .../emailpassword/EmailPasswordStorage.java | 4 ++- .../EmailVerificationStorage.java | 3 +- .../EmailVerificationSQLStorage.java | 4 ++- .../multitenancy/AppIdentifier.java | 32 ++++++++++++++++++- .../multitenancy/TenantIdentifier.java | 32 ++++++++++++++++++- .../TenantOrAppNotFoundException.java | 9 +++--- .../passwordless/PasswordlessStorage.java | 5 +-- .../sqlStorage/PasswordlessSQLStorage.java | 9 +++++- .../thirdparty/ThirdPartyStorage.java | 4 ++- 9 files changed, 88 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index eb535fed..d34f36dc 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -24,12 +24,14 @@ 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; public interface EmailPasswordStorage extends AuthRecipeStorage { // we pass tenantIdentifier here cause this also adds to the userId <-> tenantId mapping void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) - throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException; + throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, + TenantOrAppNotFoundException; // this deletion of a user is app wide since the same user ID can be shared across tenants void deleteEmailPasswordUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java index 3e2d6f10..b5bf6682 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java @@ -19,12 +19,13 @@ import io.supertokens.pluginInterface.emailverification.exception.DuplicateEmailVerificationTokenException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; public interface EmailVerificationStorage extends NonAuthRecipeStorage { void addEmailVerificationToken(AppIdentifier appIdentifier, EmailVerificationTokenInfo emailVerificationInfo) - throws StorageQueryException, DuplicateEmailVerificationTokenException; + throws StorageQueryException, DuplicateEmailVerificationTokenException, TenantOrAppNotFoundException; EmailVerificationTokenInfo getEmailVerificationTokenInfo(AppIdentifier appIdentifier, String token) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java index b6739880..b23b4aaf 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java @@ -20,6 +20,7 @@ import io.supertokens.pluginInterface.emailverification.EmailVerificationTokenInfo; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; @@ -36,6 +37,7 @@ void deleteAllEmailVerificationTokensForUser_Transaction(AppIdentifier appIdenti void updateIsEmailVerified_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, String email, - boolean isEmailVerified) throws StorageQueryException; + boolean isEmailVerified) + throws StorageQueryException, TenantOrAppNotFoundException; } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java index daf9a4aa..7a17da75 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java @@ -16,6 +16,17 @@ package io.supertokens.pluginInterface.multitenancy; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; +import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; +import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; +import io.supertokens.pluginInterface.session.SessionStorage; +import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; +import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; +import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -29,9 +40,19 @@ public class AppIdentifier { @Nullable private final String appId; + @Nullable + private Storage storage; + public AppIdentifier(@Nullable String connectionUriDomain, @Nullable String appId) { this.connectionUriDomain = connectionUriDomain; this.appId = appId; + this.storage = null; + } + + public AppIdentifier(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable Storage storage) { + this.connectionUriDomain = connectionUriDomain; + this.appId = appId; + this.storage = storage; } @Nonnull @@ -50,6 +71,15 @@ public String getConnectionUriDomain() { return this.connectionUriDomain.trim().toLowerCase(); } + public void setStorage(Storage storage) { + this.storage = storage; + } + + @Nullable + public Storage getStorage() { + return this.storage; + } + @Override public boolean equals(Object other) { if (other instanceof AppIdentifier) { @@ -67,6 +97,6 @@ public int hashCode() { } public TenantIdentifier getAsPublicTenantIdentifier() { - return new TenantIdentifier(this.getConnectionUriDomain(), this.getAppId(), null); + return new TenantIdentifier(this.getConnectionUriDomain(), this.getAppId(), null, this.getStorage()); } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java index 6b1913c0..a0fc9414 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -16,6 +16,12 @@ package io.supertokens.pluginInterface.multitenancy; +import io.supertokens.pluginInterface.STORAGE_TYPE; +import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; +import io.supertokens.pluginInterface.emailpassword.EmailPasswordStorage; +import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -33,10 +39,21 @@ public class TenantIdentifier { @Nullable private final String appId; + @Nullable + private Storage storage; + public TenantIdentifier(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable String tenantId) { this.connectionUriDomain = connectionUriDomain; this.tenantId = tenantId; this.appId = appId; + this.storage = null; + } + + public TenantIdentifier(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable String tenantId, @Nullable Storage storage) { + this.connectionUriDomain = connectionUriDomain; + this.tenantId = tenantId; + this.appId = appId; + this.storage = storage; } @Nonnull @@ -63,6 +80,11 @@ public String getConnectionUriDomain() { return this.connectionUriDomain.trim().toLowerCase(); } + @Nullable + public Storage getStorage() { + return this.storage; + } + @Override public boolean equals(Object other) { if (other instanceof TenantIdentifier) { @@ -81,6 +103,14 @@ public int hashCode() { } public AppIdentifier toAppIdentifier() { - return new AppIdentifier(this.getConnectionUriDomain(), this.getAppId()); + return new AppIdentifier(this.getConnectionUriDomain(), this.getAppId(), this.getStorage()); + } + + public AppIdentifier toAppIdentifier(Storage storage) { + return new AppIdentifier(this.getConnectionUriDomain(), this.getAppId(), storage); + } + + public void setStorage(Storage storage) { + this.storage = storage; } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/TenantOrAppNotFoundException.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/TenantOrAppNotFoundException.java index 0bc67f5e..0dc258ad 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/TenantOrAppNotFoundException.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/exceptions/TenantOrAppNotFoundException.java @@ -16,19 +16,18 @@ package io.supertokens.pluginInterface.multitenancy.exceptions; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; public class TenantOrAppNotFoundException extends Exception { - private final TenantIdentifier tenantIdentifier; - public TenantOrAppNotFoundException(TenantIdentifier tenantIdentifier) { super("Tenant with the following connectionURIDomain, appId and tenantId combination not found: (" + tenantIdentifier.getConnectionUriDomain() + ", " + tenantIdentifier.getAppId() + ", " + tenantIdentifier.getTenantId() + ")"); - this.tenantIdentifier = tenantIdentifier; } - public TenantIdentifier getTenantIdentifier() { - return this.tenantIdentifier; + public TenantOrAppNotFoundException(AppIdentifier appIdentifier) { + super("App with the following connectionURIDomain and appId combination not found: (" + + appIdentifier.getConnectionUriDomain() + ", " + appIdentifier.getAppId() + ")"); } } diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java index 1f53bf40..334c353e 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java @@ -22,6 +22,7 @@ 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.pluginInterface.passwordless.exception.*; import javax.annotation.Nonnull; @@ -31,14 +32,14 @@ public interface PasswordlessStorage extends AuthRecipeStorage { void createDeviceWithCode(TenantIdentifier tenantIdentifier, @Nullable String email, @Nullable String phoneNumber, @Nonnull String linkCodeSalt, PasswordlessCode code) throws StorageQueryException, DuplicateDeviceIdHashException, - DuplicateCodeIdException, DuplicateLinkCodeHashException; + DuplicateCodeIdException, DuplicateLinkCodeHashException, TenantOrAppNotFoundException; void createCode(TenantIdentifier tenantIdentifier, PasswordlessCode code) throws StorageQueryException, UnknownDeviceIdHash, DuplicateCodeIdException, DuplicateLinkCodeHashException; void createUser(TenantIdentifier tenantIdentifier, UserInfo user) throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, - DuplicateUserIdException; + DuplicateUserIdException, TenantOrAppNotFoundException; void deletePasswordlessUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java index ef47754f..42a79c25 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java @@ -59,10 +59,17 @@ void deleteDevicesByEmail_Transaction(TenantIdentifier tenantIdentifier, Transac throws StorageQueryException; void deleteDevicesByPhoneNumber_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + String phoneNumber) + throws StorageQueryException; + + void deleteDevicesByEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String email) + throws StorageQueryException; + + void deleteDevicesByPhoneNumber_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String phoneNumber, String userId) throws StorageQueryException; - void deleteDevicesByEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String email, + void deleteDevicesByEmail_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String email, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java index 950b744c..873a8605 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java @@ -20,6 +20,7 @@ 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.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException; import io.supertokens.pluginInterface.thirdparty.exception.DuplicateUserIdException; @@ -28,7 +29,8 @@ public interface ThirdPartyStorage extends AuthRecipeStorage { void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) - throws StorageQueryException, DuplicateUserIdException, DuplicateThirdPartyUserException; + throws StorageQueryException, DuplicateUserIdException, DuplicateThirdPartyUserException, + TenantOrAppNotFoundException; void deleteThirdPartyUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; From 35d7cdef56d6d46c854fce7f7a12c4a99748622a Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 24 Mar 2023 16:49:09 +0530 Subject: [PATCH 43/93] fix: fixed pless interface (#64) --- .../passwordless/sqlStorage/PasswordlessSQLStorage.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java index 42a79c25..ef47754f 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java @@ -59,17 +59,10 @@ void deleteDevicesByEmail_Transaction(TenantIdentifier tenantIdentifier, Transac throws StorageQueryException; void deleteDevicesByPhoneNumber_Transaction(AppIdentifier appIdentifier, TransactionConnection con, - String phoneNumber) - throws StorageQueryException; - - void deleteDevicesByEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String email) - throws StorageQueryException; - - void deleteDevicesByPhoneNumber_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String phoneNumber, String userId) throws StorageQueryException; - void deleteDevicesByEmail_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String email, + void deleteDevicesByEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String email, String userId) throws StorageQueryException; From 0ad9103d2f8e9c0e508a2ef0a816294a21da4eef Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Sat, 25 Mar 2023 11:27:24 +0530 Subject: [PATCH 44/93] fix: to support PR comments on core (#65) * fix: from core pr comments * fix: TenantIdentifierWithStorage and AppIdentifierWithStorage * fix: made storage private --- .../authRecipe/AuthRecipeStorage.java | 3 ++ .../multitenancy/AppIdentifier.java | 25 +++-------- .../AppIdentifierWithStorage.java | 39 +++++++++++++++++ .../multitenancy/TenantIdentifier.java | 31 ++----------- .../TenantIdentifierWithStorage.java | 43 +++++++++++++++++++ .../useridmapping/UserIdMappingStorage.java | 3 +- 6 files changed, 95 insertions(+), 49 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java index abb12f3c..c1bea6d3 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java @@ -31,6 +31,9 @@ public interface AuthRecipeStorage extends Storage { long getUsersCount(TenantIdentifier tenantIdentifier, @Nullable RECIPE_ID[] includeRecipeIds) throws StorageQueryException; + long getUsersCount(AppIdentifier appIdentifier, @Nullable RECIPE_ID[] includeRecipeIds) + throws StorageQueryException; + AuthRecipeUserInfo[] getUsers(TenantIdentifier tenantIdentifier, @Nonnull Integer limit, @Nonnull String timeJoinedOrder, @Nullable RECIPE_ID[] includeRecipeIds, @Nullable String userId, diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java index 7a17da75..0aa4aedf 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifier.java @@ -40,19 +40,9 @@ public class AppIdentifier { @Nullable private final String appId; - @Nullable - private Storage storage; - public AppIdentifier(@Nullable String connectionUriDomain, @Nullable String appId) { this.connectionUriDomain = connectionUriDomain; this.appId = appId; - this.storage = null; - } - - public AppIdentifier(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable Storage storage) { - this.connectionUriDomain = connectionUriDomain; - this.appId = appId; - this.storage = storage; } @Nonnull @@ -71,15 +61,6 @@ public String getConnectionUriDomain() { return this.connectionUriDomain.trim().toLowerCase(); } - public void setStorage(Storage storage) { - this.storage = storage; - } - - @Nullable - public Storage getStorage() { - return this.storage; - } - @Override public boolean equals(Object other) { if (other instanceof AppIdentifier) { @@ -97,6 +78,10 @@ public int hashCode() { } public TenantIdentifier getAsPublicTenantIdentifier() { - return new TenantIdentifier(this.getConnectionUriDomain(), this.getAppId(), null, this.getStorage()); + return new TenantIdentifier(this.getConnectionUriDomain(), this.getAppId(), null); + } + + public AppIdentifierWithStorage withStorage(Storage storage) { + return new AppIdentifierWithStorage(this.getConnectionUriDomain(), this.getAppId(), storage); } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java new file mode 100644 index 00000000..322b8906 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +import io.supertokens.pluginInterface.Storage; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AppIdentifierWithStorage extends AppIdentifier { + + @Nonnull + private final Storage storage; + + public AppIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, @Nonnull + Storage storage) { + super(connectionUriDomain, appId); + this.storage = storage; + } + + @Nonnull + public Storage getStorage() { + return storage; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java index a0fc9414..a870e11d 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -16,11 +16,7 @@ package io.supertokens.pluginInterface.multitenancy; -import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; -import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; -import io.supertokens.pluginInterface.emailpassword.EmailPasswordStorage; -import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -39,21 +35,10 @@ public class TenantIdentifier { @Nullable private final String appId; - @Nullable - private Storage storage; - public TenantIdentifier(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable String tenantId) { this.connectionUriDomain = connectionUriDomain; this.tenantId = tenantId; this.appId = appId; - this.storage = null; - } - - public TenantIdentifier(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable String tenantId, @Nullable Storage storage) { - this.connectionUriDomain = connectionUriDomain; - this.tenantId = tenantId; - this.appId = appId; - this.storage = storage; } @Nonnull @@ -80,11 +65,6 @@ public String getConnectionUriDomain() { return this.connectionUriDomain.trim().toLowerCase(); } - @Nullable - public Storage getStorage() { - return this.storage; - } - @Override public boolean equals(Object other) { if (other instanceof TenantIdentifier) { @@ -103,14 +83,11 @@ public int hashCode() { } public AppIdentifier toAppIdentifier() { - return new AppIdentifier(this.getConnectionUriDomain(), this.getAppId(), this.getStorage()); - } - - public AppIdentifier toAppIdentifier(Storage storage) { - return new AppIdentifier(this.getConnectionUriDomain(), this.getAppId(), storage); + return new AppIdentifier(this.getConnectionUriDomain(), this.getAppId()); } - public void setStorage(Storage storage) { - this.storage = storage; + public TenantIdentifierWithStorage withStorage(Storage storage) { + return new TenantIdentifierWithStorage(this.getConnectionUriDomain(), this.getAppId(), this.getTenantId(), + storage); } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java new file mode 100644 index 00000000..037eed9b --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +import io.supertokens.pluginInterface.Storage; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class TenantIdentifierWithStorage extends TenantIdentifier { + + @Nonnull + private final Storage storage; + + public TenantIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable String tenantId, @Nonnull + Storage storage) { + super(connectionUriDomain, appId, tenantId); + this.storage = storage; + } + + @Nonnull + public Storage getStorage() { + return storage; + } + + public AppIdentifierWithStorage toAppIdentifierWithStorage() { + return new AppIdentifierWithStorage(this.getConnectionUriDomain(), this.getAppId(), this.getStorage()); + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java index eab19ae9..7b1ebd4c 100644 --- a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java @@ -50,7 +50,6 @@ boolean updateOrDeleteExternalUserIdInfo(AppIdentifier appIdentifier, String use // This function will be used to retrieve the userId mapping for a list of userIds. The key of the HashMap will be // superTokensUserId and the value will be the externalUserId. If a mapping does not exist for an input userId, // it will not be in a part of the returned HashMap - HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier, - ArrayList userIds) throws StorageQueryException; + HashMap getUserIdMappingForSuperTokensIds(ArrayList userIds) throws StorageQueryException; } From 546af6a2971fa31b788566121f19a41531bd1d6b Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 27 Mar 2023 12:31:12 +0530 Subject: [PATCH 45/93] fix: added storages to appIdentifierWithStorage (#66) --- .../multitenancy/AppIdentifierWithStorage.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 322b8906..c59a4eed 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -26,14 +26,29 @@ public class AppIdentifierWithStorage extends AppIdentifier { @Nonnull private final Storage storage; + private final Storage[] storages; + public AppIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, @Nonnull Storage storage) { super(connectionUriDomain, appId); this.storage = storage; + this.storages = new Storage[]{storage}; + } + + public AppIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, @Nonnull + Storage storage, @Nonnull Storage[] storages) { + super(connectionUriDomain, appId); + this.storage = storage; + this.storages = storages; } @Nonnull public Storage getStorage() { return storage; } + + @Nonnull + public Storage[] getStorages() { + return storages; + } } From d6dc7e05a4223e9258ea998341905a0e54307b04 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 28 Mar 2023 15:49:07 +0530 Subject: [PATCH 46/93] fix: Multitenant userroles (#67) * fix: user roles impl * fix: handling fkey * Update src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java --------- Co-authored-by: Rishabh Poddar --- .../multitenancy/AppIdentifierWithStorage.java | 10 ++++++++++ .../multitenancy/TenantIdentifierWithStorage.java | 10 ++++++++++ .../pluginInterface/userroles/UserRolesStorage.java | 4 +++- .../userroles/sqlStorage/UserRolesSQLStorage.java | 7 +++---- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index c59a4eed..2bdbb82f 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -16,7 +16,9 @@ package io.supertokens.pluginInterface.multitenancy; +import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -51,4 +53,12 @@ public Storage getStorage() { public Storage[] getStorages() { return storages; } + + public UserRolesSQLStorage getUserRolesStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (UserRolesSQLStorage) this.storage; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index 037eed9b..b5ccbda4 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -16,7 +16,9 @@ package io.supertokens.pluginInterface.multitenancy; +import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -40,4 +42,12 @@ public Storage getStorage() { public AppIdentifierWithStorage toAppIdentifierWithStorage() { return new AppIdentifierWithStorage(this.getConnectionUriDomain(), this.getAppId(), this.getStorage()); } + + public UserRolesSQLStorage getUserRolesStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (UserRolesSQLStorage) this.storage; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java b/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java index 3b816e44..e080df6f 100644 --- a/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/userroles/UserRolesStorage.java @@ -19,6 +19,7 @@ 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.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; import io.supertokens.pluginInterface.userroles.exception.DuplicateUserRoleMappingException; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; @@ -27,7 +28,8 @@ public interface UserRolesStorage extends NonAuthRecipeStorage { // associate a userId with a role that exists void addRoleToUser(TenantIdentifier tenantIdentifier, String userId, String role) - throws StorageQueryException, UnknownRoleException, DuplicateUserRoleMappingException; + throws StorageQueryException, UnknownRoleException, DuplicateUserRoleMappingException, + TenantOrAppNotFoundException; // get all roles associated with the input userId String[] getRolesForUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java index 18c78f25..a887ee33 100644 --- a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java @@ -19,6 +19,7 @@ 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.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.userroles.UserRolesStorage; @@ -31,11 +32,9 @@ boolean deleteRoleForUser_Transaction(TenantIdentifier tenantIdentifier, Transac String role) throws StorageQueryException; - // create a new role if it doesnt exist. The reason this has tenantIdentifier is that it also adds the - // tenantId <-> role mapping - boolean createNewRoleOrDoNothingIfExists_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + boolean createNewRoleOrDoNothingIfExists_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) - throws StorageQueryException; + throws StorageQueryException, TenantOrAppNotFoundException; // associate a permission with a role void addPermissionToRoleOrDoNothingIfExists_Transaction(AppIdentifier appIdentifier, TransactionConnection con, From a9d7e0f0c7fa84229d174fe1d4cd338dee66e505 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 29 Mar 2023 11:09:13 +0530 Subject: [PATCH 47/93] fix: Multitenant usermetadata (#68) * fix: user roles impl * fix: handling fkey * fix: usermetadata impl * fix: user metadata impl --- .../multitenancy/AppIdentifierWithStorage.java | 10 ++++++++++ .../sqlStorage/UserMetadataSQLStorage.java | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 2bdbb82f..a858ef17 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; import javax.annotation.Nonnull; @@ -54,6 +55,15 @@ public Storage[] getStorages() { return storages; } + public UserMetadataSQLStorage getUserMetadataStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + + return (UserMetadataSQLStorage) this.storage; + } + public UserRolesSQLStorage getUserRolesStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java index 6ffa1383..99d5aaf2 100644 --- a/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java @@ -19,6 +19,7 @@ import com.google.gson.JsonObject; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.usermetadata.UserMetadataStorage; @@ -29,5 +30,5 @@ JsonObject getUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionC int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, JsonObject metadata) - throws StorageQueryException; + throws StorageQueryException, TenantOrAppNotFoundException; } From 77cf4f39132554d2e87188a2191a204d6b1b414e Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 29 Mar 2023 12:16:12 +0530 Subject: [PATCH 48/93] fix: ep storage (#69) --- .../multitenancy/AppIdentifierWithStorage.java | 9 +++++++++ .../multitenancy/TenantIdentifierWithStorage.java | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index a858ef17..26c10621 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; @@ -55,6 +56,14 @@ public Storage[] getStorages() { return storages; } + public EmailPasswordSQLStorage getEmailPasswordStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (EmailPasswordSQLStorage) this.storage; + } + public UserMetadataSQLStorage getUserMetadataStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index b5ccbda4..4ff23559 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -18,6 +18,8 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; import javax.annotation.Nonnull; @@ -43,6 +45,14 @@ public AppIdentifierWithStorage toAppIdentifierWithStorage() { return new AppIdentifierWithStorage(this.getConnectionUriDomain(), this.getAppId(), this.getStorage()); } + public EmailPasswordSQLStorage getEmailPasswordStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (EmailPasswordSQLStorage) this.storage; + } + public UserRolesSQLStorage getUserRolesStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 32ff39dee57258f9f1654231b739364ea3cb5776 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 29 Mar 2023 13:02:00 +0530 Subject: [PATCH 49/93] fix: Multitenant uidmapping storage (#70) * fix: uid mapping storage * fix: SQL check --- .../multitenancy/AppIdentifierWithStorage.java | 10 ++++++++++ .../multitenancy/TenantIdentifierWithStorage.java | 12 +++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 26c10621..6ab7a559 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; @@ -73,6 +74,15 @@ public UserMetadataSQLStorage getUserMetadataStorage() { return (UserMetadataSQLStorage) this.storage; } + public UserIdMappingStorage getUserIdMappingStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + + return (UserIdMappingStorage) this.storage; + } + public UserRolesSQLStorage getUserRolesStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index 4ff23559..abd8c47c 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -18,8 +18,8 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; -import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; import javax.annotation.Nonnull; @@ -45,11 +45,21 @@ public AppIdentifierWithStorage toAppIdentifierWithStorage() { return new AppIdentifierWithStorage(this.getConnectionUriDomain(), this.getAppId(), this.getStorage()); } + public UserIdMappingStorage getUserIdMappingStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + + return (UserIdMappingStorage) this.storage; + } + public EmailPasswordSQLStorage getEmailPasswordStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now throw new UnsupportedOperationException(""); } + return (EmailPasswordSQLStorage) this.storage; } From c91dd33ee8fdb03681fdfb5e730a273109d46ffa Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 31 Mar 2023 13:44:45 +0530 Subject: [PATCH 50/93] fix: Multitenant passwordless storage (#71) * fix: passwordless storage * fix: passwordless storage --- .../multitenancy/AppIdentifierWithStorage.java | 9 +++++++++ .../multitenancy/TenantIdentifierWithStorage.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 6ab7a559..72970a61 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; @@ -65,6 +66,14 @@ public EmailPasswordSQLStorage getEmailPasswordStorage() { return (EmailPasswordSQLStorage) this.storage; } + public PasswordlessSQLStorage getPasswordlessStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (PasswordlessSQLStorage) this.storage; + } + public UserMetadataSQLStorage getUserMetadataStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index abd8c47c..0f5741df 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; @@ -63,6 +64,14 @@ public EmailPasswordSQLStorage getEmailPasswordStorage() { return (EmailPasswordSQLStorage) this.storage; } + public PasswordlessSQLStorage getPasswordlessStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (PasswordlessSQLStorage) this.storage; + } + public UserRolesSQLStorage getUserRolesStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 05cba25f19aaabafe9c3f23ed4178df7291d3218 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 31 Mar 2023 17:51:47 +0530 Subject: [PATCH 51/93] fix: thirdparty storage (#72) --- .../multitenancy/AppIdentifierWithStorage.java | 9 +++++++++ .../multitenancy/TenantIdentifierWithStorage.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 72970a61..1a094895 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -19,6 +19,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; +import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; @@ -74,6 +75,14 @@ public PasswordlessSQLStorage getPasswordlessStorage() { return (PasswordlessSQLStorage) this.storage; } + public ThirdPartySQLStorage getThirdPartyStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (ThirdPartySQLStorage) this.storage; + } + public UserMetadataSQLStorage getUserMetadataStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index 0f5741df..0b08e022 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -19,6 +19,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; +import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; @@ -72,6 +73,14 @@ public PasswordlessSQLStorage getPasswordlessStorage() { return (PasswordlessSQLStorage) this.storage; } + public ThirdPartySQLStorage getThirdPartyStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (ThirdPartySQLStorage) this.storage; + } + public UserRolesSQLStorage getUserRolesStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 2e97a56e5f13fc097d9bb7d9760934bcd9d07259 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Sat, 1 Apr 2023 11:01:50 +0530 Subject: [PATCH 52/93] fix: Multitenant thirdparty changes for update email (#73) * fix: thirdparty storage * fix: thirdparty changes --- .../thirdparty/sqlStorage/ThirdPartySQLStorage.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java index 0d23a29f..08260679 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java @@ -17,7 +17,7 @@ package io.supertokens.pluginInterface.thirdparty.sqlStorage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.thirdparty.ThirdPartyStorage; @@ -25,11 +25,11 @@ public interface ThirdPartySQLStorage extends ThirdPartyStorage, SQLStorage { - UserInfo getUserInfoUsingId_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, + UserInfo getUserInfoUsingId_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String thirdPartyId, String thirdPartyUserId) throws StorageQueryException; - void updateUserEmail_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String thirdPartyId, + void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String thirdPartyId, String thirdPartyUserId, String newEmail) throws StorageQueryException; } From 16a9f589c7a479fd3c537a4c71eed5d09d5bd200 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 3 Apr 2023 14:25:58 +0530 Subject: [PATCH 53/93] fix: Multitenant emailverification storage (#74) * fix: thirdparty storage * fix: emailverification storage --- .../multitenancy/AppIdentifierWithStorage.java | 9 +++++++++ .../multitenancy/TenantIdentifierWithStorage.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 1a094895..e07f862d 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; @@ -83,6 +84,14 @@ public ThirdPartySQLStorage getThirdPartyStorage() { return (ThirdPartySQLStorage) this.storage; } + public EmailVerificationSQLStorage getEmailVerificationStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (EmailVerificationSQLStorage) this.storage; + } + public UserMetadataSQLStorage getUserMetadataStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index 0b08e022..fc450a9a 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; @@ -81,6 +82,14 @@ public ThirdPartySQLStorage getThirdPartyStorage() { return (ThirdPartySQLStorage) this.storage; } + public EmailVerificationSQLStorage getEmailVerificationStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (EmailVerificationSQLStorage) this.storage; + } + public UserRolesSQLStorage getUserRolesStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 6b010589421c9afc7e74725aa9afd77f887eed19 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 3 Apr 2023 17:09:15 +0530 Subject: [PATCH 54/93] fix: making tokens tenant specific (#75) --- .../emailverification/EmailVerificationStorage.java | 9 +++++---- .../sqlStorage/EmailVerificationSQLStorage.java | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java index b5bf6682..723b5e3e 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java @@ -19,26 +19,27 @@ import io.supertokens.pluginInterface.emailverification.exception.DuplicateEmailVerificationTokenException; 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.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; public interface EmailVerificationStorage extends NonAuthRecipeStorage { - void addEmailVerificationToken(AppIdentifier appIdentifier, EmailVerificationTokenInfo emailVerificationInfo) + void addEmailVerificationToken(TenantIdentifier tenantIdentifier, EmailVerificationTokenInfo emailVerificationInfo) throws StorageQueryException, DuplicateEmailVerificationTokenException, TenantOrAppNotFoundException; - EmailVerificationTokenInfo getEmailVerificationTokenInfo(AppIdentifier appIdentifier, String token) + EmailVerificationTokenInfo getEmailVerificationTokenInfo(TenantIdentifier tenantIdentifier, String token) throws StorageQueryException; void deleteEmailVerificationUserInfo(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - void revokeAllTokens(AppIdentifier appIdentifier, String userId, String email) throws StorageQueryException; + void revokeAllTokens(TenantIdentifier tenantIdentifier, String userId, String email) throws StorageQueryException; void unverifyEmail(AppIdentifier appIdentifier, String userId, String email) throws StorageQueryException; void deleteExpiredEmailVerificationTokens() throws StorageQueryException; - EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser(AppIdentifier appIdentifier, String userId, + EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser(TenantIdentifier tenantIdentifier, String userId, String email) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java index b23b4aaf..14ceb117 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java @@ -20,18 +20,19 @@ import io.supertokens.pluginInterface.emailverification.EmailVerificationTokenInfo; 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.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface EmailVerificationSQLStorage extends EmailVerificationStorage, SQLStorage { - EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser_Transaction(AppIdentifier appIdentifier, + EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId, String email) throws StorageQueryException; - void deleteAllEmailVerificationTokensForUser_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + void deleteAllEmailVerificationTokensForUser_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId, String email) throws StorageQueryException; From 7acf7f5d57f2c4547b0c747a048f1ee6a36b708b Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Wed, 5 Apr 2023 12:39:15 +0530 Subject: [PATCH 55/93] comment modification --- src/main/java/io/supertokens/pluginInterface/Storage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index efff6864..30a61698 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -37,8 +37,9 @@ public interface Storage { // two different user pool IDs imply that the data for those two user pools are completely isolated. String getUserPoolId(); - // this returns a unique ID based on the db's connection connection pool config. This can be different + // this returns a unique ID based on the db's connection pool config. This can be different // even if the getUserPoolId returns the same ID - based on the config provided by the user. + // So two different db connection pools may point to the same end user pool. String getConnectionPoolId(); // if the input otherConfig has different values set for the same properties as this user pool's config, From 03bd13a4b9686ef6e90730fbff647452336fddc9 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 5 Apr 2023 16:36:30 +0530 Subject: [PATCH 56/93] fix: Multitenant session (#76) * fix: session changes * fix: session changes --- .../multitenancy/TenantIdentifierWithStorage.java | 5 +++++ .../supertokens/pluginInterface/session/SessionStorage.java | 4 +++- .../session/sqlStorage/SessionSQLStorage.java | 5 +++-- .../supertokens/pluginInterface/sqlStorage/SQLStorage.java | 3 ++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index fc450a9a..aa17727e 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -20,6 +20,7 @@ import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; +import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; @@ -90,6 +91,10 @@ public EmailVerificationSQLStorage getEmailVerificationStorage() { return (EmailVerificationSQLStorage) this.storage; } + public SessionStorage getSessionStorage() { + return (SessionStorage) this.storage; + } + public UserRolesSQLStorage getUserRolesStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java index f5bb1825..555c607e 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java @@ -20,6 +20,7 @@ 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.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; import javax.annotation.Nullable; @@ -27,7 +28,8 @@ public interface SessionStorage extends NonAuthRecipeStorage { void createNewSession(TenantIdentifier tenantIdentifier, String sessionHandle, String userId, String refreshTokenHash2, JsonObject userDataInDatabase, - long expiry, JsonObject userDataInJWT, long createdAtTime) throws StorageQueryException; + long expiry, JsonObject userDataInJWT, long createdAtTime) throws StorageQueryException, + TenantOrAppNotFoundException; void deleteSessionsOfUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java index de5bd741..c84bc3a2 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java @@ -20,6 +20,7 @@ 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.pluginInterface.session.SessionInfo; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; @@ -38,14 +39,14 @@ KeyValueInfo[] getAccessTokenSigningKeys_Transaction(AppIdentifier appIdentifier void addAccessTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con, KeyValueInfo info) - throws StorageQueryException; + throws StorageQueryException, TenantOrAppNotFoundException; KeyValueInfo getRefreshTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con) throws StorageQueryException; void setRefreshTokenSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con, KeyValueInfo info) - throws StorageQueryException; + throws StorageQueryException, TenantOrAppNotFoundException; SessionInfo getSessionInfo_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String sessionHandle) diff --git a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java index e8d5e919..d1351b30 100644 --- a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java @@ -22,6 +22,7 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; public interface SQLStorage extends Storage { T startTransaction(TransactionLogic logic, TransactionIsolationLevel isolationLevel) @@ -32,7 +33,7 @@ T startTransaction(TransactionLogic logic, TransactionIsolationLevel isol void commitTransaction(TransactionConnection con) throws StorageQueryException; void setKeyValue_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String key, - KeyValueInfo info) throws StorageQueryException; + KeyValueInfo info) throws StorageQueryException, TenantOrAppNotFoundException; KeyValueInfo getKeyValue_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String key) throws StorageQueryException; From a93b6b9fed762af3c19aa23f018e0ac9ad403c76 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 5 Apr 2023 17:13:12 +0530 Subject: [PATCH 57/93] fix: adding tenant or app not found exceptions --- src/main/java/io/supertokens/pluginInterface/Storage.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 30a61698..6efbf027 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -23,6 +23,7 @@ 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 java.util.Set; @@ -63,7 +64,8 @@ public interface Storage { KeyValueInfo getKeyValue(TenantIdentifier tenantIdentifier, String key) throws StorageQueryException; - void setKeyValue(TenantIdentifier tenantIdentifier, String key, KeyValueInfo info) throws StorageQueryException; + void setKeyValue(TenantIdentifier tenantIdentifier, String key, KeyValueInfo info) throws StorageQueryException, + TenantOrAppNotFoundException; void setStorageLayerEnabled(boolean enabled); From 7d9a1338cf8b21776a0fc931e6ce2355738fa21e Mon Sep 17 00:00:00 2001 From: Rishabh Poddar Date: Wed, 5 Apr 2023 18:17:28 +0530 Subject: [PATCH 58/93] merges with latest (#77) --- CHANGELOG.md | 9 ++ build.gradle | 2 +- ...2.20.0.jar => plugin-interface-2.22.0.jar} | Bin 58735 -> 67132 bytes .../pluginInterface/ActiveUsersStorage.java | 18 ++++ .../pluginInterface/RECIPE_ID.java | 2 +- .../authRecipe/AuthRecipeStorage.java | 3 +- .../dashboard/DashboardSearchTags.java | 96 ++++++++++++++++++ .../AppIdentifierWithStorage.java | 24 ++++- .../TenantIdentifierWithStorage.java | 18 +++- .../pluginInterface/totp/TOTPDevice.java | 38 +++++++ .../pluginInterface/totp/TOTPStorage.java | 41 ++++++++ .../pluginInterface/totp/TOTPUsedCode.java | 36 +++++++ .../DeviceAlreadyExistsException.java | 5 + .../exception/TotpNotEnabledException.java | 5 + .../exception/UnknownDeviceException.java | 5 + .../UsedCodeAlreadyExistsException.java | 5 + .../totp/sqlStorage/TOTPSQLStorage.java | 39 +++++++ 17 files changed, 337 insertions(+), 9 deletions(-) rename jar/{plugin-interface-2.20.0.jar => plugin-interface-2.22.0.jar} (62%) create mode 100644 src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java create mode 100644 src/main/java/io/supertokens/pluginInterface/dashboard/DashboardSearchTags.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/TOTPUsedCode.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/exception/DeviceAlreadyExistsException.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/exception/TotpNotEnabledException.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/exception/UnknownDeviceException.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/exception/UsedCodeAlreadyExistsException.java create mode 100644 src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java diff --git a/CHANGELOG.md b/CHANGELOG.md index d389d895..16d02302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [2.22.0] - 2023-03-30 + +- Adds Support for Dashboard Search + +## [2.21.0] - 2023-03-27 + +- Introduce TOTP Recipe plugin interface +- Introduce Active users storage plugin interface + ## [2.20.0] - 2023-02-21 - Dashboard Recipe Interface diff --git a/build.gradle b/build.gradle index 575d0fdd..e4b086a7 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "2.20.0" +version = "2.22.0" repositories { mavenCentral() diff --git a/jar/plugin-interface-2.20.0.jar b/jar/plugin-interface-2.22.0.jar similarity index 62% rename from jar/plugin-interface-2.20.0.jar rename to jar/plugin-interface-2.22.0.jar index 5e1be93f3d953f73d506cbfe5352a7f75e2bf3bc..2b9893e8eb79cc29c3bb2cc711c729c3de193c24 100644 GIT binary patch delta 13218 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 delta 6011 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 emails; + public ArrayList phoneNumbers; + public ArrayList providers; + + public DashboardSearchTags(@Nullable ArrayList emails, @Nullable ArrayList phones, + @Nullable ArrayList providers) { + this.emails = emails; + this.phoneNumbers = phones; + this.providers = providers; + } + + public boolean shouldEmailPasswordTableBeSearched() { + + ArrayList nonNullSearchTags = getNonNullSearchFields(); + return nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.EMAIL) && nonNullSearchTags.size() == 1; + + } + + public boolean shouldThirdPartyTableBeSearched() { + + ArrayList nonNullSearchTags = getNonNullSearchFields(); + if(nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.EMAIL) && nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.PROVIDER)){ + return nonNullSearchTags.size() == 2; + } + + if(nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.EMAIL) || nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.PROVIDER)){ + return nonNullSearchTags.size() == 1; + } + + return false; + } + + public boolean shouldPasswordlessTableBeSearched() { + ArrayList nonNullSearchTags = getNonNullSearchFields(); + if(nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.EMAIL) && nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.PHONE)){ + return nonNullSearchTags.size() == 2; + } + + if(nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.EMAIL) || nonNullSearchTags.contains(SUPPORTED_SEARCH_TAGS.PHONE)){ + return nonNullSearchTags.size() == 1; + } + + return false; + } + + private ArrayList getNonNullSearchFields() { + ArrayList nonNullSearchTags = new ArrayList<>(); + + if (this.emails != null) { + nonNullSearchTags.add(SUPPORTED_SEARCH_TAGS.EMAIL); + } + + if (this.phoneNumbers != null) { + nonNullSearchTags.add(SUPPORTED_SEARCH_TAGS.PHONE); + } + + if (this.providers != null) { + nonNullSearchTags.add(SUPPORTED_SEARCH_TAGS.PROVIDER); + } + + return nonNullSearchTags; + } + + public enum SUPPORTED_SEARCH_TAGS { + EMAIL("email"), PHONE("phone"), PROVIDER("provider"); + + private String tag; + + SUPPORTED_SEARCH_TAGS(String tag) { + this.tag = tag; + } + + public static SUPPORTED_SEARCH_TAGS fromString(String text) { + for (SUPPORTED_SEARCH_TAGS t : SUPPORTED_SEARCH_TAGS.values()) { + if (t.tag.equalsIgnoreCase(text)) { + return t; + } + } + return null; + } + + @Override + public String toString() { + return tag; + } + } + +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index e07f862d..846c95ff 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -16,13 +16,15 @@ package io.supertokens.pluginInterface.multitenancy; +import io.supertokens.pluginInterface.ActiveUsersStorage; import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; +import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; -import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; @@ -37,14 +39,14 @@ public class AppIdentifierWithStorage extends AppIdentifier { private final Storage[] storages; public AppIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, @Nonnull - Storage storage) { + Storage storage) { super(connectionUriDomain, appId); this.storage = storage; this.storages = new Storage[]{storage}; } public AppIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, @Nonnull - Storage storage, @Nonnull Storage[] storages) { + Storage storage, @Nonnull Storage[] storages) { super(connectionUriDomain, appId); this.storage = storage; this.storages = storages; @@ -117,4 +119,20 @@ public UserRolesSQLStorage getUserRolesStorage() { } return (UserRolesSQLStorage) this.storage; } + + public TOTPSQLStorage getTOTPStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (TOTPSQLStorage) this.storage; + } + + public ActiveUsersStorage getActiveUsersStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (ActiveUsersStorage) this.storage; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index aa17727e..7068e4bc 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -18,12 +18,14 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; +import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; -import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; import javax.annotation.Nonnull; @@ -34,8 +36,9 @@ public class TenantIdentifierWithStorage extends TenantIdentifier { @Nonnull private final Storage storage; - public TenantIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, @Nullable String tenantId, @Nonnull - Storage storage) { + public TenantIdentifierWithStorage(@Nullable String connectionUriDomain, @Nullable String appId, + @Nullable String tenantId, @Nonnull + Storage storage) { super(connectionUriDomain, appId, tenantId); this.storage = storage; } @@ -102,4 +105,13 @@ public UserRolesSQLStorage getUserRolesStorage() { } return (UserRolesSQLStorage) this.storage; } + + public TOTPSQLStorage getTOTPStorage() + throws TenantOrAppNotFoundException { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (TOTPSQLStorage) this.storage; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java b/src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java new file mode 100644 index 00000000..e29f091e --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java @@ -0,0 +1,38 @@ +package io.supertokens.pluginInterface.totp; + +public class TOTPDevice { + public final String deviceName; + public final String userId; + public final String secretKey; + public final int period; + public final int skew; + public final boolean verified; + + public TOTPDevice(String userId, String deviceName, String secretKey, int period, + int skew, boolean verified) { + this.userId = userId; + this.deviceName = deviceName; + this.secretKey = secretKey; + this.period = period; + this.skew = skew; + this.verified = verified; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (!(obj instanceof TOTPDevice)) { + return false; + } + TOTPDevice other = (TOTPDevice) obj; + return this.userId.equals(other.userId) && + this.deviceName.equals(other.deviceName) + && this.secretKey.equals(other.secretKey) && this.period == other.period && this.skew == other.skew + && this.verified == other.verified; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java b/src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java new file mode 100644 index 00000000..88ce0aa7 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java @@ -0,0 +1,41 @@ +package io.supertokens.pluginInterface.totp; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; +import io.supertokens.pluginInterface.totp.exception.DeviceAlreadyExistsException; +import io.supertokens.pluginInterface.totp.exception.UnknownDeviceException; + +public interface TOTPStorage extends NonAuthRecipeStorage { + /** + * Create a new device and a new user if the user does not exist: + */ + void createDevice(AppIdentifier appIdentifier, TOTPDevice device) + throws StorageQueryException, DeviceAlreadyExistsException; + + /** + * Verify a user's device with the given name: + */ + void markDeviceAsVerified(AppIdentifier appIdentifier, String userId, String deviceName) + throws StorageQueryException, UnknownDeviceException; + + /** + * Update device name of a device: + */ + void updateDeviceName(AppIdentifier appIdentifier, String userId, String oldDeviceName, String newDeviceName) + throws StorageQueryException, DeviceAlreadyExistsException, + UnknownDeviceException; + + /** + * Get the devices for a user + */ + TOTPDevice[] getDevices(AppIdentifier appIdentifier, String userId) + throws StorageQueryException; + + /** + * Remove expired codes from totp used codes for all users: + */ + int removeExpiredCodes(TenantIdentifier tenantIdentifier, long expiredBefore) + throws StorageQueryException; +} diff --git a/src/main/java/io/supertokens/pluginInterface/totp/TOTPUsedCode.java b/src/main/java/io/supertokens/pluginInterface/totp/TOTPUsedCode.java new file mode 100644 index 00000000..62ff6ea2 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/TOTPUsedCode.java @@ -0,0 +1,36 @@ +package io.supertokens.pluginInterface.totp; + +public class TOTPUsedCode { + public final String userId; + public final String code; + public final boolean isValid; + public final long expiryTime; + public final long createdTime; + + public TOTPUsedCode(String userId, String code, Boolean isValidCode, + long expiryTime, + long createdTime) { + this.userId = userId; + this.code = code; + this.isValid = isValidCode; + this.expiryTime = expiryTime; + this.createdTime = createdTime; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (!(obj instanceof TOTPUsedCode)) { + return false; + } + TOTPUsedCode other = (TOTPUsedCode) obj; + return this.userId.equals(other.userId) && this.code.equals(other.code) + && this.isValid == other.isValid && this.expiryTime == other.expiryTime + && this.createdTime == other.createdTime; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/totp/exception/DeviceAlreadyExistsException.java b/src/main/java/io/supertokens/pluginInterface/totp/exception/DeviceAlreadyExistsException.java new file mode 100644 index 00000000..edf61122 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/exception/DeviceAlreadyExistsException.java @@ -0,0 +1,5 @@ +package io.supertokens.pluginInterface.totp.exception; + +public class DeviceAlreadyExistsException extends Exception { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/totp/exception/TotpNotEnabledException.java b/src/main/java/io/supertokens/pluginInterface/totp/exception/TotpNotEnabledException.java new file mode 100644 index 00000000..bb702ef5 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/exception/TotpNotEnabledException.java @@ -0,0 +1,5 @@ +package io.supertokens.pluginInterface.totp.exception; + +public class TotpNotEnabledException extends Exception { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/totp/exception/UnknownDeviceException.java b/src/main/java/io/supertokens/pluginInterface/totp/exception/UnknownDeviceException.java new file mode 100644 index 00000000..86bf659a --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/exception/UnknownDeviceException.java @@ -0,0 +1,5 @@ +package io.supertokens.pluginInterface.totp.exception; + +public class UnknownDeviceException extends Exception { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/totp/exception/UsedCodeAlreadyExistsException.java b/src/main/java/io/supertokens/pluginInterface/totp/exception/UsedCodeAlreadyExistsException.java new file mode 100644 index 00000000..7ada2a2f --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/exception/UsedCodeAlreadyExistsException.java @@ -0,0 +1,5 @@ +package io.supertokens.pluginInterface.totp.exception; + +public class UsedCodeAlreadyExistsException extends Exception { + +} diff --git a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java new file mode 100644 index 00000000..46d4db53 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java @@ -0,0 +1,39 @@ +package io.supertokens.pluginInterface.totp.sqlStorage; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.sqlStorage.SQLStorage; +import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import io.supertokens.pluginInterface.totp.TOTPDevice; +import io.supertokens.pluginInterface.totp.TOTPStorage; +import io.supertokens.pluginInterface.totp.TOTPUsedCode; +import io.supertokens.pluginInterface.totp.exception.TotpNotEnabledException; +import io.supertokens.pluginInterface.totp.exception.UsedCodeAlreadyExistsException; + +public interface TOTPSQLStorage extends TOTPStorage, SQLStorage { + public int deleteDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, + String deviceName) + throws StorageQueryException; + + public TOTPDevice[] getDevices_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) + throws StorageQueryException; + + public void removeUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) + throws StorageQueryException; + + /** + * Get totp used codes for user (expired/non-expired) yet (sorted by descending + * order of created time): + */ + public TOTPUsedCode[] getAllUsedCodesDescOrder_Transaction(TransactionConnection con, + TenantIdentifier tenantIdentifier, String userId) + throws StorageQueryException; + + /** + * Insert a used TOTP code for an existing user: + */ + void insertUsedCode_Transaction(TransactionConnection con, TenantIdentifier tenantIdentifier, TOTPUsedCode code) + throws StorageQueryException, TotpNotEnabledException, UsedCodeAlreadyExistsException; + +} From 0c160569429a45fce944807c8f88ed883cda6957 Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Thu, 6 Apr 2023 18:50:43 +0530 Subject: [PATCH 59/93] small change --- src/main/java/io/supertokens/pluginInterface/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 6efbf027..496b01b5 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -52,7 +52,7 @@ public interface Storage { void stopLogging(); // load tables and create connection pools - void initStorage() throws DbInitException; + void initStorage(boolean shouldWait) throws DbInitException; // used by the core to do transactions the right way. STORAGE_TYPE getType(); From 89bc5f7a93b332d4c8717d124ca40caa11ee3941 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 6 Apr 2023 19:16:20 +0530 Subject: [PATCH 60/93] fix: jwt changes (#78) --- .../pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java index 00b4d1c2..3ab19c79 100644 --- a/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/jwt/sqlstorage/JWTRecipeSQLStorage.java @@ -21,6 +21,7 @@ import io.supertokens.pluginInterface.jwt.JWTSigningKeyInfo; import io.supertokens.pluginInterface.jwt.exceptions.DuplicateKeyIdException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; @@ -31,5 +32,5 @@ List getJWTSigningKeys_Transaction(AppIdentifier appIdentifie throws StorageQueryException; void setJWTSigningKey_Transaction(AppIdentifier appIdentifier, TransactionConnection con, JWTSigningKeyInfo info) - throws StorageQueryException, DuplicateKeyIdException; + throws StorageQueryException, DuplicateKeyIdException, TenantOrAppNotFoundException; } From c072fa67676b9cac620d062fe4bb8ed878e84585 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 7 Apr 2023 17:43:13 +0530 Subject: [PATCH 61/93] fix: Multitenant Authrecipe changes (#79) * fix: auth recipe storage * fix: added session storage --- .../multitenancy/AppIdentifierWithStorage.java | 15 +++++++++++++++ .../multitenancy/TenantIdentifierWithStorage.java | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 846c95ff..67ebec5f 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -19,9 +19,11 @@ import io.supertokens.pluginInterface.ActiveUsersStorage; import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; +import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; @@ -62,6 +64,15 @@ public Storage[] getStorages() { return storages; } + public AuthRecipeStorage getAuthRecipeStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + + return (AuthRecipeStorage) this.storage; + } + public EmailPasswordSQLStorage getEmailPasswordStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now @@ -94,6 +105,10 @@ public EmailVerificationSQLStorage getEmailVerificationStorage() { return (EmailVerificationSQLStorage) this.storage; } + public SessionStorage getSessionStorage() { + return (SessionStorage) this.storage; + } + public UserMetadataSQLStorage getUserMetadataStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index 7068e4bc..b5e04279 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -18,6 +18,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; @@ -52,6 +53,15 @@ public AppIdentifierWithStorage toAppIdentifierWithStorage() { return new AppIdentifierWithStorage(this.getConnectionUriDomain(), this.getAppId(), this.getStorage()); } + public AuthRecipeStorage getAuthRecipeStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + + return (AuthRecipeStorage) this.storage; + } + public UserIdMappingStorage getUserIdMappingStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 525e80ccfcb7804a0eab67b26767cf66a8bf0183 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 10 Apr 2023 16:07:16 +0530 Subject: [PATCH 62/93] fix: Multitenant dashboard (#80) * fix: dashboard storage * fix: updated exception --- .../pluginInterface/dashboard/DashboardStorage.java | 4 +++- .../multitenancy/AppIdentifierWithStorage.java | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java b/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java index 41e8061a..83c85cf6 100644 --- a/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/dashboard/DashboardStorage.java @@ -6,11 +6,13 @@ import io.supertokens.pluginInterface.dashboard.exceptions.UserIdNotFoundException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; public interface DashboardStorage extends Storage { void createNewDashboardUser(AppIdentifier appIdentifier, DashboardUser userInfo) - throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException; + throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, + TenantOrAppNotFoundException; DashboardUser getDashboardUserByEmail(AppIdentifier appIdentifier, String email) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 67ebec5f..1134d51d 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -20,6 +20,7 @@ import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; +import io.supertokens.pluginInterface.dashboard.sqlStorage.DashboardSQLStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; @@ -135,6 +136,14 @@ public UserRolesSQLStorage getUserRolesStorage() { return (UserRolesSQLStorage) this.storage; } + public DashboardSQLStorage getDashboardStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (DashboardSQLStorage) this.storage; + } + public TOTPSQLStorage getTOTPStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 566f5a8a827553c40768936c632fa932e804599a Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 11 Apr 2023 16:44:23 +0530 Subject: [PATCH 63/93] fix: Multitenant totp (#81) * fix: removed unused throw * fix: handling fk --- .../multitenancy/TenantIdentifierWithStorage.java | 3 +-- .../java/io/supertokens/pluginInterface/totp/TOTPStorage.java | 3 ++- .../pluginInterface/totp/sqlStorage/TOTPSQLStorage.java | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index b5e04279..b278395e 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -116,8 +116,7 @@ public UserRolesSQLStorage getUserRolesStorage() { return (UserRolesSQLStorage) this.storage; } - public TOTPSQLStorage getTOTPStorage() - throws TenantOrAppNotFoundException { + public TOTPSQLStorage getTOTPStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now throw new UnsupportedOperationException(""); diff --git a/src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java b/src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java index 88ce0aa7..ab8cbbae 100644 --- a/src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/totp/TOTPStorage.java @@ -3,6 +3,7 @@ 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.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; import io.supertokens.pluginInterface.totp.exception.DeviceAlreadyExistsException; import io.supertokens.pluginInterface.totp.exception.UnknownDeviceException; @@ -12,7 +13,7 @@ public interface TOTPStorage extends NonAuthRecipeStorage { * Create a new device and a new user if the user does not exist: */ void createDevice(AppIdentifier appIdentifier, TOTPDevice device) - throws StorageQueryException, DeviceAlreadyExistsException; + throws StorageQueryException, DeviceAlreadyExistsException, TenantOrAppNotFoundException; /** * Verify a user's device with the given name: diff --git a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java index 46d4db53..77083e25 100644 --- a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java @@ -3,6 +3,7 @@ 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.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.totp.TOTPDevice; @@ -34,6 +35,7 @@ public TOTPUsedCode[] getAllUsedCodesDescOrder_Transaction(TransactionConnection * Insert a used TOTP code for an existing user: */ void insertUsedCode_Transaction(TransactionConnection con, TenantIdentifier tenantIdentifier, TOTPUsedCode code) - throws StorageQueryException, TotpNotEnabledException, UsedCodeAlreadyExistsException; + throws StorageQueryException, TotpNotEnabledException, UsedCodeAlreadyExistsException, + TenantOrAppNotFoundException; } From 7019947de139c425e67f2ede6cedeb9431620fd4 Mon Sep 17 00:00:00 2001 From: Rishabh Poddar Date: Thu, 13 Apr 2023 11:01:06 +0530 Subject: [PATCH 64/93] merges (#82) --- CHANGELOG.md | 5 +++++ build.gradle | 2 +- jar/plugin-interface-2.23.0.jar | Bin 0 -> 67290 bytes .../jwt/JWTAsymmetricSigningKeyInfo.java | 2 +- .../pluginInterface/jwt/JWTSigningKeyInfo.java | 9 +++++++++ .../pluginInterface/session/SessionInfo.java | 4 +++- .../pluginInterface/session/SessionStorage.java | 3 ++- .../SessionInfoWithLastUpdated.java | 4 ++-- 8 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 jar/plugin-interface-2.23.0.jar diff --git a/CHANGELOG.md b/CHANGELOG.md index 16d02302..0b6cfb59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [2.23.0] - 2023-04-05 + +- Added `useStaticKey ` into session info classes +- Added `hashCode` override for JWTSigningKeyInfo + ## [2.22.0] - 2023-03-30 - Adds Support for Dashboard Search diff --git a/build.gradle b/build.gradle index e4b086a7..96d08997 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "2.22.0" +version = "2.23.0" repositories { mavenCentral() diff --git a/jar/plugin-interface-2.23.0.jar b/jar/plugin-interface-2.23.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..7edb243ecff36fc52e276504b1595b98e94074b6 GIT binary patch literal 67290 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 32)); + result = 31 * result + algorithm.hashCode(); + result = 31 * result + keyString.hashCode(); + return result; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/session/SessionInfo.java b/src/main/java/io/supertokens/pluginInterface/session/SessionInfo.java index 00b1f563..50b670e8 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/SessionInfo.java +++ b/src/main/java/io/supertokens/pluginInterface/session/SessionInfo.java @@ -26,9 +26,10 @@ public class SessionInfo { public long expiry; public JsonObject userDataInJWT; public long timeCreated; + public transient boolean useStaticKey; public SessionInfo(String sessionHandle, String userId, String refreshTokenHash2, JsonObject userDataInDatabase, - long expiry, JsonObject userDataInJWT, long timeCreated) { + long expiry, JsonObject userDataInJWT, long timeCreated, boolean useStaticKey) { this.sessionHandle = sessionHandle; this.userId = userId; this.refreshTokenHash2 = refreshTokenHash2; @@ -36,5 +37,6 @@ public SessionInfo(String sessionHandle, String userId, String refreshTokenHash2 this.expiry = expiry; this.userDataInJWT = userDataInJWT; this.timeCreated = timeCreated; + this.useStaticKey = useStaticKey; } } diff --git a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java index 555c607e..88312e90 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java @@ -28,7 +28,8 @@ public interface SessionStorage extends NonAuthRecipeStorage { void createNewSession(TenantIdentifier tenantIdentifier, String sessionHandle, String userId, String refreshTokenHash2, JsonObject userDataInDatabase, - long expiry, JsonObject userDataInJWT, long createdAtTime) throws StorageQueryException, + long expiry, JsonObject userDataInJWT, long createdAtTime, boolean useStaticKey) + throws StorageQueryException, TenantOrAppNotFoundException; void deleteSessionsOfUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionInfoWithLastUpdated.java b/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionInfoWithLastUpdated.java index 0dddba71..d96be4d5 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionInfoWithLastUpdated.java +++ b/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionInfoWithLastUpdated.java @@ -23,9 +23,9 @@ public class SessionInfoWithLastUpdated extends SessionInfo { public String lastUpdatedSign; public SessionInfoWithLastUpdated(String sessionHandle, String userId, String refreshTokenHash2, - JsonObject userDataInDatabase, long expiry, JsonObject userDataInJWT, long timeCreated, + JsonObject userDataInDatabase, long expiry, JsonObject userDataInJWT, long timeCreated, boolean useStaticKey, String lastUpdatedSign) { - super(sessionHandle, userId, refreshTokenHash2, userDataInDatabase, expiry, userDataInJWT, timeCreated); + super(sessionHandle, userId, refreshTokenHash2, userDataInDatabase, expiry, userDataInJWT, timeCreated, useStaticKey); this.lastUpdatedSign = lastUpdatedSign; } } From 155aa16bb7e545f8fd99764d145ed5f405f32611 Mon Sep 17 00:00:00 2001 From: Rishabh Poddar Date: Tue, 18 Apr 2023 17:57:22 +0530 Subject: [PATCH 65/93] adds new config (#83) --- src/main/java/io/supertokens/pluginInterface/Storage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 496b01b5..b08b6fc8 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -82,4 +82,8 @@ boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, String cla // this function is used during testing in the core so that the core can // create multiple user pools across any plugin being used. void modifyConfigToAddANewUserPoolForTesting(JsonObject config, int poolNumber); + + // this function returns a list of protected configs which users of supertokens saas can't read or modify + // when they are operating on tenantsm unless the supertokens_saas_secret key is used in the API request. + String[] getProtectedConfigsFromSuperTokensSaaSUsers(); } From 3cd30edae07fac4711285bdd1ab51925c3fe158a Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 20 Apr 2023 17:28:27 +0530 Subject: [PATCH 66/93] fix: multitenancy changes (#84) * fix: multitenancy * fix: interface update * fix: added db protection flag * fix: add userId to tenant * fix: updated as per CDI * fix: removed unused func * fix: protected fields * fix: pr comment * fix: better serialization --- .../pluginInterface/RECIPE_ID.java | 3 +- .../multitenancy/EmailPasswordConfig.java | 2 +- .../multitenancy/MultitenancyStorage.java | 28 +++++++++---------- .../multitenancy/TenantConfig.java | 23 +++++++++++++++ .../TenantIdentifierWithStorage.java | 8 ++++++ 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java b/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java index 7456f85f..c7fb396f 100644 --- a/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java +++ b/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java @@ -21,7 +21,8 @@ public enum RECIPE_ID { EMAIL_PASSWORD("emailpassword"), THIRD_PARTY("thirdparty"), SESSION("session"), EMAIL_VERIFICATION("emailverification"), JWT("jwt"), PASSWORDLESS("passwordless"), USER_METADATA("usermetadata"), - USER_ROLES("userroles"), USER_ID_MAPPING("useridmapping"), DASHBOARD("dashboard"), TOTP("totp"); + USER_ROLES("userroles"), USER_ID_MAPPING("useridmapping"), DASHBOARD("dashboard"), TOTP("totp"), + MULTITENANCY("multitenancy"); private final String name; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java index 01d25929..aac0c636 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/EmailPasswordConfig.java @@ -17,7 +17,7 @@ package io.supertokens.pluginInterface.multitenancy; public class EmailPasswordConfig { - public boolean enabled; + public final boolean enabled; public EmailPasswordConfig(boolean enabled) { this.enabled = enabled; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index 25953fb3..89ab71c7 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -17,12 +17,15 @@ package io.supertokens.pluginInterface.multitenancy; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateClientTypeException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException; import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; +import io.supertokens.pluginInterface.passwordless.exception.DuplicatePhoneNumberException; +import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; public interface MultitenancyStorage extends Storage { @@ -31,31 +34,28 @@ void createTenant(TenantConfig config) throws DuplicateTenantException, Duplicat DuplicateClientTypeException, StorageQueryException; // this adds tenantId to the target user pool - void addTenantIdInUserPool(TenantIdentifier tenantIdentifier) + void addTenantIdInTargetStorage(TenantIdentifier tenantIdentifier) throws DuplicateTenantException, StorageQueryException; // this also deletes all tenant info from all tables. - void deleteTenantIdInUserPool(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; + void deleteTenantIdInTargetStorage(TenantIdentifier tenantIdentifier) + throws TenantOrAppNotFoundException, StorageQueryException; void overwriteTenantConfig(TenantConfig config) throws TenantOrAppNotFoundException, DuplicateThirdPartyIdException, DuplicateClientTypeException, StorageQueryException; - void deleteTenant(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; + boolean deleteTenantInfoInBaseStorage(TenantIdentifier tenantIdentifier) throws StorageQueryException; - void deleteApp(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; + boolean deleteAppInfoInBaseStorage(AppIdentifier appIdentifier) throws StorageQueryException; - void deleteConnectionUriDomainMapping(TenantIdentifier tenantIdentifier) throws TenantOrAppNotFoundException; + boolean deleteConnectionUriDomainInfoInBaseStorage(String connectionUriDomain) throws StorageQueryException; TenantConfig[] getAllTenants() throws StorageQueryException; - void addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws TenantOrAppNotFoundException, - UnknownUserIdException; - - void addRoleToTenant(TenantIdentifier tenantIdentifier, String role) throws TenantOrAppNotFoundException, - UnknownRoleException; - - void deleteAppId(String appId) throws TenantOrAppNotFoundException; - - void deleteConnectionUriDomain(String connectionUriDomain) throws TenantOrAppNotFoundException; + boolean addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) throws TenantOrAppNotFoundException, + UnknownUserIdException, StorageQueryException, DuplicateEmailException, DuplicateThirdPartyUserException, + DuplicatePhoneNumberException; + boolean removeUserIdFromTenant(TenantIdentifier tenantIdentifier, String userId) + throws StorageQueryException, UnknownUserIdException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index ba456bca..c4f7617d 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -16,7 +16,10 @@ package io.supertokens.pluginInterface.multitenancy; +import com.google.gson.Gson; import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import io.supertokens.pluginInterface.Storage; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -27,12 +30,15 @@ public class TenantConfig { public transient final TenantIdentifier tenantIdentifier; @Nonnull + @SerializedName("emailPassword") public final EmailPasswordConfig emailPasswordConfig; @Nonnull + @SerializedName("thirdParty") public final ThirdPartyConfig thirdPartyConfig; @Nonnull + @SerializedName("passwordless") public final PasswordlessConfig passwordlessConfig; @Nonnull @@ -72,4 +78,21 @@ public boolean equals(Object other) { public int hashCode() { return tenantIdentifier.hashCode(); } + + public JsonObject toJson(boolean shouldProtectDbConfig, Storage storage) { + Gson gson = new Gson(); + JsonObject tenantConfigObject = gson.toJsonTree(this).getAsJsonObject(); + tenantConfigObject.addProperty("tenantId", this.tenantIdentifier.getTenantId()); + + if (shouldProtectDbConfig) { + String[] protectedConfigs = storage.getProtectedConfigsFromSuperTokensSaaSUsers(); + for (String config : protectedConfigs) { + if (tenantConfigObject.get("coreConfig").getAsJsonObject().has(config)) { + tenantConfigObject.get("coreConfig").getAsJsonObject().remove(config); + } + } + } + + return tenantConfigObject; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index b278395e..82002f0c 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -123,4 +123,12 @@ public TOTPSQLStorage getTOTPStorage() { } return (TOTPSQLStorage) this.storage; } + + public MultitenancyStorage getMultitenancyStorageWithTargetStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (MultitenancyStorage) this.storage; + } } From a67d94df05fb816a4695a1e5309daded3c26d570 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 25 Apr 2023 13:37:23 +0530 Subject: [PATCH 67/93] fix: base tenant (#85) --- .../pluginInterface/multitenancy/TenantIdentifier.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java index a870e11d..9abac35a 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifier.java @@ -26,6 +26,8 @@ public class TenantIdentifier { public static final String DEFAULT_APP_ID = "public"; public static final String DEFAULT_CONNECTION_URI = ""; + public static TenantIdentifier BASE_TENANT = new TenantIdentifier(null, null, null); + @Nullable private final String connectionUriDomain; From eb0bf7ae8cc2b3fb287605a5a800b5650bee4cd1 Mon Sep 17 00:00:00 2001 From: KShivendu Date: Thu, 27 Apr 2023 11:54:15 +0530 Subject: [PATCH 68/93] feat: Introduce MFA recipe --- .../supertokens/pluginInterface/RECIPE_ID.java | 2 +- .../pluginInterface/mfa/MfaStorage.java | 16 ++++++++++++++++ .../TenantIdentifierWithStorage.java | 10 +++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java diff --git a/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java b/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java index c7fb396f..7bafb724 100644 --- a/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java +++ b/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java @@ -22,7 +22,7 @@ public enum RECIPE_ID { EMAIL_PASSWORD("emailpassword"), THIRD_PARTY("thirdparty"), SESSION("session"), EMAIL_VERIFICATION("emailverification"), JWT("jwt"), PASSWORDLESS("passwordless"), USER_METADATA("usermetadata"), USER_ROLES("userroles"), USER_ID_MAPPING("useridmapping"), DASHBOARD("dashboard"), TOTP("totp"), - MULTITENANCY("multitenancy"); + MULTITENANCY("multitenancy"), MFA("mfa"); private final String name; diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java new file mode 100644 index 00000000..ed056392 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java @@ -0,0 +1,16 @@ +package io.supertokens.pluginInterface.mfa; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; + +public interface MfaStorage extends NonAuthRecipeStorage { + // Enable (insert) a factor for a user and return true if it actually inserted something. + boolean enableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; + + // List all the factors for a user: + String[] listFactors(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + + // Disable (delete) a factor for a user and return true if it actually deleted something. + boolean disableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index 82002f0c..190a791e 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -21,7 +21,7 @@ import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; -import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; +import io.supertokens.pluginInterface.mfa.MfaStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; @@ -124,6 +124,14 @@ public TOTPSQLStorage getTOTPStorage() { return (TOTPSQLStorage) this.storage; } + public MfaStorage getMfaStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (MfaStorage) this.storage; + } + public MultitenancyStorage getMultitenancyStorageWithTargetStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 57382645b7b9788a92da236e36f6d5d037a189b6 Mon Sep 17 00:00:00 2001 From: KShivendu Date: Thu, 27 Apr 2023 11:56:45 +0530 Subject: [PATCH 69/93] chores: Mention MFA recipe in the CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b6cfb59..f5c95d92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Introduce MFA Recipe plugin interface + ## [2.23.0] - 2023-04-05 - Added `useStaticKey ` into session info classes From a8537c804a945591df1569df50945404f340342e Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 27 Apr 2023 13:13:59 +0530 Subject: [PATCH 70/93] fix: Tenantid in userobjects (#86) * fix: adding tenant ids to user objects * fix: create user type * fix: refactor ep and tp * fix: refactor pless --- .../pluginInterface/authRecipe/AuthRecipeUserInfo.java | 5 ++++- .../pluginInterface/emailpassword/EmailPasswordStorage.java | 2 +- .../supertokens/pluginInterface/emailpassword/UserInfo.java | 4 ++-- .../pluginInterface/passwordless/PasswordlessStorage.java | 2 +- .../supertokens/pluginInterface/passwordless/UserInfo.java | 4 ++-- .../pluginInterface/thirdparty/ThirdPartyStorage.java | 2 +- .../io/supertokens/pluginInterface/thirdparty/UserInfo.java | 4 ++-- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeUserInfo.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeUserInfo.java index 6ae64cf3..29a60aee 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeUserInfo.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeUserInfo.java @@ -24,9 +24,12 @@ public abstract class AuthRecipeUserInfo { public long timeJoined; - public AuthRecipeUserInfo(String id, long timeJoined) { + public final String[] tenantIds; + + public AuthRecipeUserInfo(String id, long timeJoined, String[] tenantIds) { this.id = id; this.timeJoined = timeJoined; + this.tenantIds = tenantIds; } public abstract RECIPE_ID getRecipeId(); diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index d34f36dc..13e789aa 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -29,7 +29,7 @@ public interface EmailPasswordStorage extends AuthRecipeStorage { // we pass tenantIdentifier here cause this also adds to the userId <-> tenantId mapping - void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) + UserInfo signUp(TenantIdentifier tenantIdentifier, String id, String email, String passwordHash, long timeJoined) throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, TenantOrAppNotFoundException; diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/UserInfo.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/UserInfo.java index 03aaf2c8..bc65964a 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/UserInfo.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/UserInfo.java @@ -26,8 +26,8 @@ public class UserInfo extends AuthRecipeUserInfo { // using transient, we tell Gson not to include this when creating a JSON public transient final String passwordHash; - public UserInfo(String id, String email, String passwordHash, long timeJoined) { - super(id, timeJoined); + public UserInfo(String id, String email, String passwordHash, long timeJoined, String[] tenantIds) { + super(id, timeJoined, tenantIds); this.email = email; this.passwordHash = passwordHash; } diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java index 334c353e..e9e6eae1 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java @@ -37,7 +37,7 @@ void createDeviceWithCode(TenantIdentifier tenantIdentifier, @Nullable String em void createCode(TenantIdentifier tenantIdentifier, PasswordlessCode code) throws StorageQueryException, UnknownDeviceIdHash, DuplicateCodeIdException, DuplicateLinkCodeHashException; - void createUser(TenantIdentifier tenantIdentifier, UserInfo user) + UserInfo createUser(TenantIdentifier tenantIdentifier, String id, @Nullable String email, @Nullable String phoneNumber, long timeJoined) throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, DuplicateUserIdException, TenantOrAppNotFoundException; diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/UserInfo.java b/src/main/java/io/supertokens/pluginInterface/passwordless/UserInfo.java index 0bd392db..9e8579b6 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/UserInfo.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/UserInfo.java @@ -25,8 +25,8 @@ public class UserInfo extends AuthRecipeUserInfo { public final String email; public final String phoneNumber; - public UserInfo(String id, @Nullable String email, @Nullable String phoneNumber, long timeJoined) { - super(id, timeJoined); + public UserInfo(String id, @Nullable String email, @Nullable String phoneNumber, long timeJoined, String[] tenantIds) { + super(id, timeJoined, tenantIds); if (email == null && phoneNumber == null) { throw new IllegalArgumentException("Both email and phoneNumber cannot be null"); diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java index 873a8605..22f51256 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyStorage.java @@ -28,7 +28,7 @@ public interface ThirdPartyStorage extends AuthRecipeStorage { - void signUp(TenantIdentifier tenantIdentifier, UserInfo userInfo) + UserInfo signUp(TenantIdentifier tenantIdentifier, String id, String email, UserInfo.ThirdParty thirdParty, long timeJoined) throws StorageQueryException, DuplicateUserIdException, DuplicateThirdPartyUserException, TenantOrAppNotFoundException; diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/UserInfo.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/UserInfo.java index b1d2d5cb..ef85520e 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/UserInfo.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/UserInfo.java @@ -25,8 +25,8 @@ public class UserInfo extends AuthRecipeUserInfo { public final String email; - public UserInfo(String id, String email, ThirdParty thirdParty, long timeJoined) { - super(id, timeJoined); + public UserInfo(String id, String email, ThirdParty thirdParty, long timeJoined, String[] tenantIds) { + super(id, timeJoined, tenantIds); this.thirdParty = thirdParty; this.email = email; } From b41dd3b1f40a6174a4e480f3edf69a261aab8b75 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Fri, 28 Apr 2023 13:51:39 +0530 Subject: [PATCH 71/93] fix: tenant id in loadConfig (#88) --- src/main/java/io/supertokens/pluginInterface/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index b08b6fc8..81977a16 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -32,7 +32,7 @@ public interface Storage { // if silent is true, do not log anything out on the console void constructor(String processId, boolean silent); - void loadConfig(JsonObject jsonConfig, Set logLevels) throws InvalidConfigException; + void loadConfig(JsonObject jsonConfig, Set logLevels, TenantIdentifier tenantIdentifier) throws InvalidConfigException; // this returns a unique ID based on the db's connection URI and table prefix such that // two different user pool IDs imply that the data for those two user pools are completely isolated. From 9b6a415c01499bd1452a6270eb9d07bf1120c199 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 2 May 2023 15:34:10 +0530 Subject: [PATCH 72/93] fix: delete non auth user (#89) --- .../emailverification/EmailVerificationStorage.java | 2 ++ .../io/supertokens/pluginInterface/session/SessionStorage.java | 2 ++ .../pluginInterface/totp/sqlStorage/TOTPSQLStorage.java | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java index 723b5e3e..1d436ef5 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java @@ -33,6 +33,8 @@ EmailVerificationTokenInfo getEmailVerificationTokenInfo(TenantIdentifier tenant void deleteEmailVerificationUserInfo(AppIdentifier appIdentifier, String userId) throws StorageQueryException; + boolean deleteEmailVerificationUserInfo(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + void revokeAllTokens(TenantIdentifier tenantIdentifier, String userId, String email) throws StorageQueryException; void unverifyEmail(AppIdentifier appIdentifier, String userId, String email) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java index 88312e90..02df9077 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/SessionStorage.java @@ -34,6 +34,8 @@ void createNewSession(TenantIdentifier tenantIdentifier, String sessionHandle, S void deleteSessionsOfUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; + boolean deleteSessionsOfUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + // return number of rows else throw UnsupportedOperationException int getNumberOfSessions(TenantIdentifier tenantIdentifier) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java index 77083e25..55d46060 100644 --- a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java @@ -23,6 +23,9 @@ public TOTPDevice[] getDevices_Transaction(TransactionConnection con, AppIdentif public void removeUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException; + public boolean removeUser(TenantIdentifier tenantIdentifier, String userId) + throws StorageQueryException; + /** * Get totp used codes for user (expired/non-expired) yet (sorted by descending * order of created time): From 77a78b0bc6cb227afb5bd315f73bfb51a38c22f7 Mon Sep 17 00:00:00 2001 From: KShivendu Date: Wed, 3 May 2023 18:12:37 +0530 Subject: [PATCH 73/93] feat: Improvements for EE features and removing MFA info when deleting the user --- .../supertokens/pluginInterface/ActiveUsersStorage.java | 6 ++++++ .../io/supertokens/pluginInterface/mfa/MfaStorage.java | 4 ++++ .../multitenancy/AppIdentifierWithStorage.java | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java index d37dedc7..154e1968 100644 --- a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java @@ -15,4 +15,10 @@ public interface ActiveUsersStorage extends Storage { /* Count the number of users who have enabled TOTP and are active */ int countUsersEnabledTotpAndActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException; + + /* Count the number of users who have enabled MFA */ + int countUsersEnabledMfa(AppIdentifier appIdentifier) throws StorageQueryException; + + /* Count the number of users who have enabled MFA and are active */ + int countUsersEnabledMfaAndActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java index ed056392..a9d16099 100644 --- a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java @@ -1,6 +1,7 @@ package io.supertokens.pluginInterface.mfa; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; @@ -13,4 +14,7 @@ public interface MfaStorage extends NonAuthRecipeStorage { // Disable (delete) a factor for a user and return true if it actually deleted something. boolean disableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; + + // Delete a user and all of their factors. + boolean deleteUser(AppIdentifier tenantIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 1134d51d..d96b5338 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -23,6 +23,7 @@ import io.supertokens.pluginInterface.dashboard.sqlStorage.DashboardSQLStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; +import io.supertokens.pluginInterface.mfa.MfaStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; @@ -152,6 +153,14 @@ public TOTPSQLStorage getTOTPStorage() { return (TOTPSQLStorage) this.storage; } + public MfaStorage getMfaStorage() { + if (this.storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (MfaStorage) this.storage; + } + public ActiveUsersStorage getActiveUsersStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From dcd65c50db8370a0ef6d84cc714e3cd86383dee6 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 4 May 2023 12:36:48 +0530 Subject: [PATCH 74/93] fix: nonAuthRecipeuserData to take tenantIdentifier (#90) --- src/main/java/io/supertokens/pluginInterface/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 81977a16..bf61724d 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -77,7 +77,7 @@ boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, String cla throws StorageQueryException; // to be used for testing purposes only. This function will add dummy data to non-auth tables. - void addInfoToNonAuthRecipesBasedOnUserId(String className, String userId) throws StorageQueryException; + void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifier, String className, String userId) throws StorageQueryException; // this function is used during testing in the core so that the core can // create multiple user pools across any plugin being used. From 9a9265be22f98a72b6c6de1cf3ca02a4e648201d Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 4 May 2023 12:48:24 +0530 Subject: [PATCH 75/93] fix: pr comment --- .../pluginInterface/multitenancy/MultitenancyStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java index 89ab71c7..5b3e05ce 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/MultitenancyStorage.java @@ -57,5 +57,5 @@ boolean addUserIdToTenant(TenantIdentifier tenantIdentifier, String userId) thro DuplicatePhoneNumberException; boolean removeUserIdFromTenant(TenantIdentifier tenantIdentifier, String userId) - throws StorageQueryException, UnknownUserIdException; + throws StorageQueryException; } \ No newline at end of file From 5b95faddbf0ec1ddbb3de1a1376a352c088a3a24 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 4 May 2023 18:48:07 +0530 Subject: [PATCH 76/93] fix: config validation (#91) --- src/main/java/io/supertokens/pluginInterface/Storage.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index bf61724d..8cf0fd16 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -86,4 +86,6 @@ boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, String cla // this function returns a list of protected configs which users of supertokens saas can't read or modify // when they are operating on tenantsm unless the supertokens_saas_secret key is used in the API request. String[] getProtectedConfigsFromSuperTokensSaaSUsers(); + + Set getValidFieldsInConfig(); } From 73201251009cdc0c82fa8e147f76e368cecc9178 Mon Sep 17 00:00:00 2001 From: KShivendu Date: Wed, 10 May 2023 18:34:18 +0530 Subject: [PATCH 77/93] feat: Add function to delete user from a tenant --- .../io/supertokens/pluginInterface/mfa/MfaStorage.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java index a9d16099..d002f012 100644 --- a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java @@ -15,6 +15,9 @@ public interface MfaStorage extends NonAuthRecipeStorage { // Disable (delete) a factor for a user and return true if it actually deleted something. boolean disableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; - // Delete a user and all of their factors. - boolean deleteUser(AppIdentifier tenantIdentifier, String userId) throws StorageQueryException; + // Delete a user across all tenants (with all the relevant factors) + boolean deleteUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; + + // Delete a user from a tenant (with all the relevant factors) + boolean deleteUserFromTenant(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; } From 1f0b25f7541b4f6fe5e0274f981e373119064aeb Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 11 May 2023 11:54:31 +0530 Subject: [PATCH 78/93] fix: reload resources (#93) --- .../pluginInterface/multitenancy/TenantConfig.java | 10 ++++++++++ .../multitenancy/ThirdPartyConfig.java | 14 +++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index c4f7617d..d5b00481 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -54,6 +54,16 @@ public TenantConfig(@Nonnull TenantIdentifier tenantIdentifier, @Nonnull EmailPa this.thirdPartyConfig = thirdPartyConfig; } + public TenantConfig(TenantConfig other) { + // copy constructor, that does a deep copy + Gson gson = new Gson(); + this.tenantIdentifier = new TenantIdentifier(other.tenantIdentifier.getConnectionUriDomain(), other.tenantIdentifier.getAppId(), other.tenantIdentifier.getTenantId()); + this.coreConfig = gson.fromJson(other.coreConfig.toString(), JsonObject.class); + this.emailPasswordConfig = new EmailPasswordConfig(other.emailPasswordConfig.enabled); + this.passwordlessConfig = new PasswordlessConfig(other.passwordlessConfig.enabled); + this.thirdPartyConfig = gson.fromJson(gson.toJsonTree(other.thirdPartyConfig).getAsJsonObject(), ThirdPartyConfig.class); + } + public boolean deepEquals(TenantConfig other) { if (other == null) { return false; diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index b0cf54e2..456dc464 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -198,16 +198,16 @@ public boolean equals(Object other) { } public static class UserInfoMapKeyValue { - @Nonnull + @Nullable public String userId; - @Nonnull + @Nullable public String email; - @Nonnull + @Nullable public String emailVerified; - public UserInfoMapKeyValue(@Nonnull String userId, @Nonnull String email, @Nonnull String emailVerified) { + public UserInfoMapKeyValue(@Nullable String userId, @Nullable String email, @Nullable String emailVerified) { this.userId = userId; this.email = email; this.emailVerified = emailVerified; @@ -217,9 +217,9 @@ public UserInfoMapKeyValue(@Nonnull String userId, @Nonnull String email, @Nonnu public boolean equals(Object other) { if (other instanceof UserInfoMapKeyValue) { UserInfoMapKeyValue otherUserInfoMapKeyValue = (UserInfoMapKeyValue) other; - return otherUserInfoMapKeyValue.userId.equals(this.userId) && - otherUserInfoMapKeyValue.email.equals(this.email) && - otherUserInfoMapKeyValue.emailVerified.equals(this.emailVerified); + return Objects.equals(otherUserInfoMapKeyValue.userId, this.userId) && + Objects.equals(otherUserInfoMapKeyValue.email, this.email) && + Objects.equals(otherUserInfoMapKeyValue.emailVerified, this.emailVerified); } return false; } From eec65fa4be8b4bb8eaa01c99399b24209543a867 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 15 May 2023 12:38:23 +0530 Subject: [PATCH 79/93] fix: added setLogLevels (#95) --- src/main/java/io/supertokens/pluginInterface/Storage.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 8cf0fd16..68c68f05 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -88,4 +88,6 @@ boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, String cla String[] getProtectedConfigsFromSuperTokensSaaSUsers(); Set getValidFieldsInConfig(); + + void setLogLevels(Set logLevels); } From 7ca0f44ad16d848ea5c123c525293409fb0848c6 Mon Sep 17 00:00:00 2001 From: KShivendu Date: Mon, 15 May 2023 18:43:28 +0530 Subject: [PATCH 80/93] Use deleteMfaInfoForuser and overload it to handle app as well as tenant identifier --- .../java/io/supertokens/pluginInterface/mfa/MfaStorage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java index d002f012..1b6eac56 100644 --- a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java @@ -16,8 +16,8 @@ public interface MfaStorage extends NonAuthRecipeStorage { boolean disableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; // Delete a user across all tenants (with all the relevant factors) - boolean deleteUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; + boolean deleteMfaInfoForUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; // Delete a user from a tenant (with all the relevant factors) - boolean deleteUserFromTenant(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; + boolean deleteMfaInfoForUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; } From 3e37980f0170660a03eb76ed20abd77117b60947 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 25 May 2023 19:23:48 +0530 Subject: [PATCH 81/93] fix: Active user storage to extend NonAuthRecipeStorage (#97) * fix: fixed base class * fix: added throws --- .../io/supertokens/pluginInterface/ActiveUsersStorage.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java index d37dedc7..4e2340cb 100644 --- a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java @@ -2,8 +2,9 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; -public interface ActiveUsersStorage extends Storage { +public interface ActiveUsersStorage extends NonAuthRecipeStorage { /* Update the last active time of a user to now */ void updateLastActive(AppIdentifier appIdentifier, String userId) throws StorageQueryException; @@ -15,4 +16,6 @@ public interface ActiveUsersStorage extends Storage { /* Count the number of users who have enabled TOTP and are active */ int countUsersEnabledTotpAndActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException; + + void deleteUserActive(AppIdentifier appIdentifier, String userId) throws StorageQueryException; } From 7dbc14ffaf42eebeac5fe9eeaccdf66f9920a66a Mon Sep 17 00:00:00 2001 From: Kumar Shivendu Date: Thu, 28 Sep 2023 16:43:09 +0530 Subject: [PATCH 82/93] refactor: Replace TotpNotEnabledError with UnknownUserIdTotpError (#106) * refactor: Replace totp not enabled error with unknown device error * chores: Update CHANGELOG * refactor: Remove totp not enabled exception * feat: Add UnknownUserIdException * Replace TotpNotEnabledError with UnknownUserIdTotpError * chores: Update CHANGELOG * fix: rename exception * fix: changelog * fix: new functions for create device --------- Co-authored-by: Sattvik Chakravarthy --- CHANGELOG.md | 4 +++- ...ledException.java => UnknownTotpUserIdException.java} | 2 +- .../pluginInterface/totp/sqlStorage/TOTPSQLStorage.java | 9 +++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) rename src/main/java/io/supertokens/pluginInterface/totp/exception/{TotpNotEnabledException.java => UnknownTotpUserIdException.java} (68%) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb2378be..82e7df8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Replace `TotpNotEnabledException` with `UnknownUserTotpIdException`. + ## [4.0.0] - 2023-09-19 - Adds support for account linking @@ -68,7 +70,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [2.22.0] - 2023-03-30 -- Adds Support for Dashboard Search +- Adds Support for Dashboard Search ## [2.21.0] - 2023-03-27 diff --git a/src/main/java/io/supertokens/pluginInterface/totp/exception/TotpNotEnabledException.java b/src/main/java/io/supertokens/pluginInterface/totp/exception/UnknownTotpUserIdException.java similarity index 68% rename from src/main/java/io/supertokens/pluginInterface/totp/exception/TotpNotEnabledException.java rename to src/main/java/io/supertokens/pluginInterface/totp/exception/UnknownTotpUserIdException.java index bb702ef5..a1dcd834 100644 --- a/src/main/java/io/supertokens/pluginInterface/totp/exception/TotpNotEnabledException.java +++ b/src/main/java/io/supertokens/pluginInterface/totp/exception/UnknownTotpUserIdException.java @@ -1,5 +1,5 @@ package io.supertokens.pluginInterface.totp.exception; -public class TotpNotEnabledException extends Exception { +public class UnknownTotpUserIdException extends Exception { private static final long serialVersionUID = 6848053563771647272L; } diff --git a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java index 55d46060..546148fb 100644 --- a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java @@ -9,8 +9,9 @@ import io.supertokens.pluginInterface.totp.TOTPDevice; import io.supertokens.pluginInterface.totp.TOTPStorage; import io.supertokens.pluginInterface.totp.TOTPUsedCode; -import io.supertokens.pluginInterface.totp.exception.TotpNotEnabledException; +import io.supertokens.pluginInterface.totp.exception.DeviceAlreadyExistsException; import io.supertokens.pluginInterface.totp.exception.UsedCodeAlreadyExistsException; +import io.supertokens.pluginInterface.totp.exception.UnknownTotpUserIdException; public interface TOTPSQLStorage extends TOTPStorage, SQLStorage { public int deleteDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, @@ -38,7 +39,11 @@ public TOTPUsedCode[] getAllUsedCodesDescOrder_Transaction(TransactionConnection * Insert a used TOTP code for an existing user: */ void insertUsedCode_Transaction(TransactionConnection con, TenantIdentifier tenantIdentifier, TOTPUsedCode code) - throws StorageQueryException, TotpNotEnabledException, UsedCodeAlreadyExistsException, + throws StorageQueryException, UnknownTotpUserIdException, UsedCodeAlreadyExistsException, TenantOrAppNotFoundException; + TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, String deviceName) throws StorageQueryException; + + TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, TOTPDevice device) + throws StorageQueryException, DeviceAlreadyExistsException, TenantOrAppNotFoundException; } From e18b86a448b22e7acec8e43aff93dadffcc3f46a Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 3 Oct 2023 16:49:57 +0530 Subject: [PATCH 83/93] fix: refactor transaction functions (#120) --- .../ActiveUsersSQLStorage.java | 28 ++++++++++++++++++ .../pluginInterface/ActiveUsersStorage.java | 4 --- .../pluginInterface/mfa/MfaStorage.java | 4 +-- .../mfa/sqlStorage/MfaSQLStorage.java | 29 +++++++++++++++++++ .../AppIdentifierWithStorage.java | 12 ++++---- 5 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/ActiveUsersSQLStorage.java create mode 100644 src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java diff --git a/src/main/java/io/supertokens/pluginInterface/ActiveUsersSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/ActiveUsersSQLStorage.java new file mode 100644 index 00000000..a093dc6a --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/ActiveUsersSQLStorage.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.sqlStorage.SQLStorage; +import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; + +public interface ActiveUsersSQLStorage extends ActiveUsersStorage, SQLStorage { + /* Delete a user from active users table */ + void deleteUserActive_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) + throws StorageQueryException; +} diff --git a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java index e2b1e7ac..8e918ee1 100644 --- a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java @@ -24,9 +24,5 @@ public interface ActiveUsersStorage extends NonAuthRecipeStorage { /* Count the number of users who have enabled MFA and are active */ int countUsersEnabledMfaAndActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException; - /* Delete a user from active users table */ - void deleteUserActive_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) - throws StorageQueryException; - int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java index 1b6eac56..77a3184c 100644 --- a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java @@ -4,6 +4,7 @@ import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; +import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface MfaStorage extends NonAuthRecipeStorage { // Enable (insert) a factor for a user and return true if it actually inserted something. @@ -15,9 +16,6 @@ public interface MfaStorage extends NonAuthRecipeStorage { // Disable (delete) a factor for a user and return true if it actually deleted something. boolean disableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; - // Delete a user across all tenants (with all the relevant factors) - boolean deleteMfaInfoForUser(AppIdentifier appIdentifier, String userId) throws StorageQueryException; - // Delete a user from a tenant (with all the relevant factors) boolean deleteMfaInfoForUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java new file mode 100644 index 00000000..087ee133 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.mfa.sqlStorage; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.mfa.MfaStorage; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.sqlStorage.SQLStorage; +import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; + +public interface MfaSQLStorage extends MfaStorage, SQLStorage { + // Delete a user across all tenants (with all the relevant factors) + boolean deleteMfaInfoForUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws + StorageQueryException; +} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 5ba6a161..904e2870 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -16,18 +16,18 @@ package io.supertokens.pluginInterface.multitenancy; -import io.supertokens.pluginInterface.ActiveUsersStorage; +import io.supertokens.pluginInterface.ActiveUsersSQLStorage; import io.supertokens.pluginInterface.STORAGE_TYPE; import io.supertokens.pluginInterface.Storage; import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; import io.supertokens.pluginInterface.dashboard.sqlStorage.DashboardSQLStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; +import io.supertokens.pluginInterface.mfa.sqlStorage.MfaSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage; -import io.supertokens.pluginInterface.mfa.MfaStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; @@ -153,19 +153,19 @@ public TOTPSQLStorage getTOTPStorage() { return (TOTPSQLStorage) this.storage; } - public MfaStorage getMfaStorage() { + public MfaSQLStorage getMfaStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now throw new UnsupportedOperationException(""); } - return (MfaStorage) this.storage; + return (MfaSQLStorage) this.storage; } - public ActiveUsersStorage getActiveUsersStorage() { + public ActiveUsersSQLStorage getActiveUsersStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now throw new UnsupportedOperationException(""); } - return (ActiveUsersStorage) this.storage; + return (ActiveUsersSQLStorage) this.storage; } } From e9ab279e2e34c2c537dd0c3a67f7fb7d3b77e71e Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Mon, 16 Oct 2023 17:13:41 +0530 Subject: [PATCH 84/93] fix: mfa cleanup (#121) --- .../supertokens/pluginInterface/mfa/MfaStorage.java | 11 ----------- .../pluginInterface/mfa/sqlStorage/MfaSQLStorage.java | 3 --- 2 files changed, 14 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java index 77a3184c..adf5b2b6 100644 --- a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java @@ -7,15 +7,4 @@ import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface MfaStorage extends NonAuthRecipeStorage { - // Enable (insert) a factor for a user and return true if it actually inserted something. - boolean enableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; - - // List all the factors for a user: - String[] listFactors(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; - - // Disable (delete) a factor for a user and return true if it actually deleted something. - boolean disableFactor(TenantIdentifier tenantIdentifier, String userId, String factorId) throws StorageQueryException; - - // Delete a user from a tenant (with all the relevant factors) - boolean deleteMfaInfoForUser(TenantIdentifier tenantIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java index 087ee133..0980fcb1 100644 --- a/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java @@ -23,7 +23,4 @@ import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface MfaSQLStorage extends MfaStorage, SQLStorage { - // Delete a user across all tenants (with all the relevant factors) - boolean deleteMfaInfoForUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws - StorageQueryException; } From 43c93f241df73cef0217e0a4bfa649476f0d7338 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 17 Oct 2023 13:55:32 +0530 Subject: [PATCH 85/93] fix: mfa cleanup (#123) --- .../pluginInterface/mfa/MfaStorage.java | 10 ------- .../mfa/sqlStorage/MfaSQLStorage.java | 26 ------------------- .../AppIdentifierWithStorage.java | 9 ------- .../TenantIdentifierWithStorage.java | 9 ------- 4 files changed, 54 deletions(-) delete mode 100644 src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java delete mode 100644 src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java deleted file mode 100644 index adf5b2b6..00000000 --- a/src/main/java/io/supertokens/pluginInterface/mfa/MfaStorage.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.supertokens.pluginInterface.mfa; - -import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.multitenancy.AppIdentifier; -import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; -import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; -import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; - -public interface MfaStorage extends NonAuthRecipeStorage { -} diff --git a/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java deleted file mode 100644 index 0980fcb1..00000000 --- a/src/main/java/io/supertokens/pluginInterface/mfa/sqlStorage/MfaSQLStorage.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.supertokens.pluginInterface.mfa.sqlStorage; - -import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.mfa.MfaStorage; -import io.supertokens.pluginInterface.multitenancy.AppIdentifier; -import io.supertokens.pluginInterface.sqlStorage.SQLStorage; -import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; - -public interface MfaSQLStorage extends MfaStorage, SQLStorage { -} diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java index 904e2870..b3c14185 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/AppIdentifierWithStorage.java @@ -23,7 +23,6 @@ import io.supertokens.pluginInterface.dashboard.sqlStorage.DashboardSQLStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; -import io.supertokens.pluginInterface.mfa.sqlStorage.MfaSQLStorage; import io.supertokens.pluginInterface.passwordless.sqlStorage.PasswordlessSQLStorage; import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; @@ -153,14 +152,6 @@ public TOTPSQLStorage getTOTPStorage() { return (TOTPSQLStorage) this.storage; } - public MfaSQLStorage getMfaStorage() { - if (this.storage.getType() != STORAGE_TYPE.SQL) { - // we only support SQL for now - throw new UnsupportedOperationException(""); - } - return (MfaSQLStorage) this.storage; - } - public ActiveUsersSQLStorage getActiveUsersStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java index 0c58c084..a422f87b 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantIdentifierWithStorage.java @@ -25,7 +25,6 @@ import io.supertokens.pluginInterface.session.SessionStorage; import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage; import io.supertokens.pluginInterface.totp.sqlStorage.TOTPSQLStorage; -import io.supertokens.pluginInterface.mfa.MfaStorage; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; import io.supertokens.pluginInterface.userroles.sqlStorage.UserRolesSQLStorage; @@ -124,14 +123,6 @@ public TOTPSQLStorage getTOTPStorage() { return (TOTPSQLStorage) this.storage; } - public MfaStorage getMfaStorage() { - if (this.storage.getType() != STORAGE_TYPE.SQL) { - // we only support SQL for now - throw new UnsupportedOperationException(""); - } - return (MfaStorage) this.storage; - } - public MultitenancyStorage getMultitenancyStorageWithTargetStorage() { if (this.storage.getType() != STORAGE_TYPE.SQL) { // we only support SQL for now From 0017637d004a6e632252584a0114a0bcbe2de442 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 26 Oct 2023 11:31:47 +0530 Subject: [PATCH 86/93] Mfa multitenancy (#122) * fix: mfa multitenancy * fix: mfa cleanup * fix: mfa first factor type * fix: mfa first factor type * fix: tenant config json * fix: interfaces changes * fix: tests * fix: config changes * fix: empty line * fix: pr comment * fix: refactor * fix: pr comments * fix: PR comments --- .../multitenancy/TenantConfig.java | 29 +++++++- .../multitenancy/ThirdPartyConfig.java | 39 ++-------- .../multitenancy/TotpConfig.java | 34 +++++++++ .../pluginInterface/utils/Utils.java | 71 +++++++++++++++++++ 4 files changed, 136 insertions(+), 37 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java create mode 100644 src/main/java/io/supertokens/pluginInterface/utils/Utils.java diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index f68ee35d..2bde4237 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -17,9 +17,11 @@ package io.supertokens.pluginInterface.multitenancy; import com.google.gson.Gson; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.annotations.SerializedName; import io.supertokens.pluginInterface.Storage; +import io.supertokens.pluginInterface.utils.Utils; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -41,17 +43,35 @@ public class TenantConfig { @SerializedName("passwordless") public final PasswordlessConfig passwordlessConfig; + @Nonnull + @SerializedName("totp") + public final TotpConfig totpConfig; + + @Nullable + @SerializedName("firstFactors") + public final String[] firstFactors; + + @Nullable + @SerializedName("defaultRequiredFactorIds") + public final String[] defaultRequiredFactorIds; + @Nonnull public final JsonObject coreConfig; public TenantConfig(@Nonnull TenantIdentifier tenantIdentifier, @Nonnull EmailPasswordConfig emailPasswordConfig, @Nonnull ThirdPartyConfig thirdPartyConfig, - @Nonnull PasswordlessConfig passwordlessConfig, @Nullable JsonObject coreConfig) { + @Nonnull PasswordlessConfig passwordlessConfig, + @Nonnull TotpConfig totpConfig, + @Nullable String[] firstFactors, @Nullable String[] defaultRequiredFactorIds, + @Nullable JsonObject coreConfig) { this.tenantIdentifier = tenantIdentifier; this.coreConfig = coreConfig == null ? new JsonObject() : coreConfig; this.emailPasswordConfig = emailPasswordConfig; this.passwordlessConfig = passwordlessConfig; this.thirdPartyConfig = thirdPartyConfig; + this.totpConfig = totpConfig; + this.firstFactors = firstFactors; + this.defaultRequiredFactorIds = defaultRequiredFactorIds; } public TenantConfig(TenantConfig other) { @@ -62,6 +82,9 @@ public TenantConfig(TenantConfig other) { this.emailPasswordConfig = new EmailPasswordConfig(other.emailPasswordConfig.enabled); this.passwordlessConfig = new PasswordlessConfig(other.passwordlessConfig.enabled); this.thirdPartyConfig = new ThirdPartyConfig(other.thirdPartyConfig.enabled, other.thirdPartyConfig.providers.clone()); + this.totpConfig = new TotpConfig(other.totpConfig.enabled); + this.firstFactors = other.firstFactors == null ? null : other.firstFactors.clone(); + this.defaultRequiredFactorIds = other.defaultRequiredFactorIds == null ? null : other.defaultRequiredFactorIds.clone(); } public boolean deepEquals(TenantConfig other) { @@ -72,6 +95,9 @@ public boolean deepEquals(TenantConfig other) { this.emailPasswordConfig.equals(other.emailPasswordConfig) && this.passwordlessConfig.equals(other.passwordlessConfig) && this.thirdPartyConfig.equals(other.thirdPartyConfig) && + this.totpConfig.equals(other.totpConfig) && + Utils.unorderedArrayEquals(this.firstFactors, other.firstFactors) && + Utils.unorderedArrayEquals(this.defaultRequiredFactorIds, other.defaultRequiredFactorIds) && this.coreConfig.equals(other.coreConfig); } @@ -94,7 +120,6 @@ public JsonObject toJson(boolean shouldProtectDbConfig, Storage storage, String[ JsonObject tenantConfigObject = gson.toJsonTree(this).getAsJsonObject(); tenantConfigObject.add("thirdParty", this.thirdPartyConfig.toJson()); - tenantConfigObject.addProperty("tenantId", this.tenantIdentifier.getTenantId()); if (shouldProtectDbConfig) { diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java index d77a3e75..9ee2352b 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/ThirdPartyConfig.java @@ -20,6 +20,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import io.supertokens.pluginInterface.utils.Utils; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -48,38 +49,6 @@ public JsonObject toJson() { return result; } - public static boolean unorderedArrayEquals(Object[] array1, Object[] array2) { - if (array1 == null && array2 == null) { - return true; - } else if (array1 == null || array2 == null) { - return false; - } - - List items1 = List.of(array1); - List items2 = new ArrayList<>(); - items2.addAll(Arrays.asList(array2)); - - if (items1.size() != items2.size()) return false; - - for (Object p1 : items1) { - boolean found = false; - for (Object p2 : items2) { - if (p1.equals(p2)) { - found = true; - break; - } - } - - if (!found) { - return false; - } else { - items2.remove(p1); - } - } - - return true; - } - public static class Provider { @Nonnull @@ -189,7 +158,7 @@ public boolean equals(Object other) { Provider otherProvider = (Provider) other; return Objects.equals(otherProvider.thirdPartyId, this.thirdPartyId) && Objects.equals(otherProvider.name, this.name) && - unorderedArrayEquals(otherProvider.clients, this.clients) && + Utils.unorderedArrayEquals(otherProvider.clients, this.clients) && Objects.equals(otherProvider.authorizationEndpoint, this.authorizationEndpoint) && Objects.equals(otherProvider.authorizationEndpointQueryParams, this.authorizationEndpointQueryParams) && @@ -245,7 +214,7 @@ public boolean equals(Object other) { return Objects.equals(otherProviderClient.clientType, this.clientType) && otherProviderClient.clientId.equals(this.clientId) && Objects.equals(otherProviderClient.clientSecret, this.clientSecret) && - unorderedArrayEquals(otherProviderClient.scope, this.scope) && + Utils.unorderedArrayEquals(otherProviderClient.scope, this.scope) && otherProviderClient.forcePKCE == this.forcePKCE && Objects.equals(otherProviderClient.additionalConfig, this.additionalConfig); } @@ -310,7 +279,7 @@ public boolean equals(Object other) { if (other instanceof ThirdPartyConfig) { ThirdPartyConfig otherThirdPartyConfig = (ThirdPartyConfig) other; return otherThirdPartyConfig.enabled == this.enabled && - unorderedArrayEquals(otherThirdPartyConfig.providers, this.providers); + Utils.unorderedArrayEquals(otherThirdPartyConfig.providers, this.providers); } return false; } diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java new file mode 100644 index 00000000..47d42349 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.multitenancy; + +public class TotpConfig { + public boolean enabled; + + public TotpConfig(boolean enabled) { + this.enabled = enabled; + } + + @Override + public boolean equals(Object other) { + if (other instanceof TotpConfig) { + TotpConfig otherTotpConfig = (TotpConfig) other; + return otherTotpConfig.enabled == this.enabled; + } + return false; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/utils/Utils.java b/src/main/java/io/supertokens/pluginInterface/utils/Utils.java new file mode 100644 index 00000000..0daa85d3 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/utils/Utils.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +public class Utils { + public static boolean unorderedStringArrayEquals(String[] arr1, String[] arr2) { + if (arr1 == null && arr2 == null) { + return true; + } + + if (arr1 == null || arr2 == null) { + return false; + } + + Set set1 = Set.of(arr1); + Set set2 = Set.of(arr2); + + return set1.equals(set2); + } + + public static boolean unorderedArrayEquals(Object[] array1, Object[] array2) { + if (array1 == null && array2 == null) { + return true; + } else if (array1 == null || array2 == null) { + return false; + } + + List items1 = List.of(array1); + List items2 = new ArrayList<>(); + items2.addAll(Arrays.asList(array2)); + + if (items1.size() != items2.size()) return false; + + for (Object p1 : items1) { + boolean found = false; + for (Object p2 : items2) { + if (p1.equals(p2)) { + found = true; + break; + } + } + + if (!found) { + return false; + } else { + items2.remove(p1); + } + } + + return true; + } +} From 712303db8e7f084314f9a427678aee43d36f6479 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 26 Oct 2023 12:33:58 +0530 Subject: [PATCH 87/93] fix: ordering in comparision --- .../pluginInterface/multitenancy/TenantConfig.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index 2bde4237..8d9d5d75 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -25,6 +25,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Objects; public class TenantConfig { @@ -96,8 +97,8 @@ public boolean deepEquals(TenantConfig other) { this.passwordlessConfig.equals(other.passwordlessConfig) && this.thirdPartyConfig.equals(other.thirdPartyConfig) && this.totpConfig.equals(other.totpConfig) && - Utils.unorderedArrayEquals(this.firstFactors, other.firstFactors) && - Utils.unorderedArrayEquals(this.defaultRequiredFactorIds, other.defaultRequiredFactorIds) && + Utils.unorderedArrayEquals(this.firstFactors, other.firstFactors) && // order is not important + Objects.deepEquals(this.defaultRequiredFactorIds, other.defaultRequiredFactorIds) && // order is important this.coreConfig.equals(other.coreConfig); } From 3c9c754922ddad4c8d8dd33e2a98dfd14448c216 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 31 Oct 2023 11:48:51 +0530 Subject: [PATCH 88/93] fix: add createdat to totp device (#130) --- .../java/io/supertokens/pluginInterface/totp/TOTPDevice.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java b/src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java index e29f091e..54c11268 100644 --- a/src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java +++ b/src/main/java/io/supertokens/pluginInterface/totp/TOTPDevice.java @@ -7,15 +7,17 @@ public class TOTPDevice { public final int period; public final int skew; public final boolean verified; + public final long createdAt; public TOTPDevice(String userId, String deviceName, String secretKey, int period, - int skew, boolean verified) { + int skew, boolean verified, long createdAt) { this.userId = userId; this.deviceName = deviceName; this.secretKey = secretKey; this.period = period; this.skew = skew; this.verified = verified; + this.createdAt = createdAt; } @Override From 17bd7c2645484fe7f61334a809189aea5c41dd85 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Tue, 31 Oct 2023 18:13:43 +0530 Subject: [PATCH 89/93] fix: mfa stats (#131) --- .../pluginInterface/ActiveUsersStorage.java | 15 ++------------- .../authRecipe/AuthRecipeStorage.java | 2 ++ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java index 8e918ee1..91fe4c21 100644 --- a/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/ActiveUsersStorage.java @@ -3,7 +3,6 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; -import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; public interface ActiveUsersStorage extends NonAuthRecipeStorage { /* Update the last active time of a user to now */ @@ -12,17 +11,7 @@ public interface ActiveUsersStorage extends NonAuthRecipeStorage { /* Count the number of users who did some activity after given timestamp */ int countUsersActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException; - /* Count the number of users who have enabled TOTP */ - int countUsersEnabledTotp(AppIdentifier appIdentifier) throws StorageQueryException; - - /* Count the number of users who have enabled TOTP and are active */ - int countUsersEnabledTotpAndActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException; - - /* Count the number of users who have enabled MFA */ - int countUsersEnabledMfa(AppIdentifier appIdentifier) throws StorageQueryException; - - /* Count the number of users who have enabled MFA and are active */ - int countUsersEnabledMfaAndActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException; - int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException; + + int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(AppIdentifier appIdentifier, long timestamp) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java index f554ffab..a4642f43 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java @@ -64,4 +64,6 @@ AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(TenantIdentifier tenantIdentif boolean checkIfUsesAccountLinking(AppIdentifier appIdentifier) throws StorageQueryException; int getUsersCountWithMoreThanOneLoginMethod(AppIdentifier appIdentifier) throws StorageQueryException; + + int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier) throws StorageQueryException; } From 14a32764c7e53b62766502ca8d4f19c80fbc0eb3 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 29 Nov 2023 17:24:50 +0530 Subject: [PATCH 90/93] fix: version and changelog --- CHANGELOG.md | 16 +++++++++++++++- build.gradle | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 267654d3..cec9ca7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,21 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -- Replace `TotpNotEnabledException` with `UnknownUserTotpIdException`. +## [4.1.0] - 2023-11-29 + +- Replace `TotpNotEnabledException` with `UnknownUserTotpIdException` +- ActiveUsersSQLStorage interface changes + - Adds `deleteUserActive_Transaction` function +- ActiveUsersStorage interface changes + - Removes `countUsersEnabledTotp`, `countUsersEnabledTotpAndActiveSince` and `deleteUserActive_Transaction` functions + - Adds `countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince` function +- AuthRecipeStorage interface changes + - Adds `getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled` function +- TenantConfig changes + - Adds `totpConfig`, `firstFactors` and `defaultRequiredFactorIds` fields +- Adds `createdAt` field to `TOTPDevice` +- TOTPSQLStorage interface changes + - Adds `getDeviceByName_Transaction` and `createDevice_Transaction` functions ## [4.0.3] - 2023-11-10 diff --git a/build.gradle b/build.gradle index bc1e6b18..8439063f 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "4.0.3" +version = "4.1.0" repositories { mavenCentral() From c1fd9748f902ece5dda4f9d64e8694f1d0dd43a8 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 29 Nov 2023 17:32:14 +0530 Subject: [PATCH 91/93] fix: version --- CHANGELOG.md | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cec9ca7d..00791f72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -## [4.1.0] - 2023-11-29 +## [5.0.0] - 2023-11-29 - Replace `TotpNotEnabledException` with `UnknownUserTotpIdException` - ActiveUsersSQLStorage interface changes diff --git a/build.gradle b/build.gradle index 8439063f..188c2b13 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "4.1.0" +version = "5.0.0" repositories { mavenCentral() From 208e1670c6f4900afc5d10c28787fa48cfca114b Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Wed, 20 Dec 2023 17:37:32 +0530 Subject: [PATCH 92/93] fix: Mfa changes (#135) * fix: remove totp config and rename fields in tenant config * fix: updated json --- CHANGELOG.md | 2 +- .../multitenancy/TenantConfig.java | 32 ++++++++--------- .../multitenancy/TotpConfig.java | 34 ------------------- 3 files changed, 16 insertions(+), 52 deletions(-) delete mode 100644 src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 00791f72..310a5b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - AuthRecipeStorage interface changes - Adds `getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled` function - TenantConfig changes - - Adds `totpConfig`, `firstFactors` and `defaultRequiredFactorIds` fields + - Adds `firstFactors` and `requiredSecondaryFactors` fields - Adds `createdAt` field to `TOTPDevice` - TOTPSQLStorage interface changes - Adds `getDeviceByName_Transaction` and `createDevice_Transaction` functions diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java index 8d9d5d75..70900423 100644 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java +++ b/src/main/java/io/supertokens/pluginInterface/multitenancy/TenantConfig.java @@ -17,7 +17,6 @@ package io.supertokens.pluginInterface.multitenancy; import com.google.gson.Gson; -import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.annotations.SerializedName; import io.supertokens.pluginInterface.Storage; @@ -25,7 +24,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Objects; public class TenantConfig { @@ -44,17 +42,13 @@ public class TenantConfig { @SerializedName("passwordless") public final PasswordlessConfig passwordlessConfig; - @Nonnull - @SerializedName("totp") - public final TotpConfig totpConfig; - @Nullable @SerializedName("firstFactors") public final String[] firstFactors; @Nullable - @SerializedName("defaultRequiredFactorIds") - public final String[] defaultRequiredFactorIds; + @SerializedName("requiredSecondaryFactors") + public final String[] requiredSecondaryFactors; @Nonnull public final JsonObject coreConfig; @@ -62,17 +56,15 @@ public class TenantConfig { public TenantConfig(@Nonnull TenantIdentifier tenantIdentifier, @Nonnull EmailPasswordConfig emailPasswordConfig, @Nonnull ThirdPartyConfig thirdPartyConfig, @Nonnull PasswordlessConfig passwordlessConfig, - @Nonnull TotpConfig totpConfig, - @Nullable String[] firstFactors, @Nullable String[] defaultRequiredFactorIds, + @Nullable String[] firstFactors, @Nullable String[] requiredSecondaryFactors, @Nullable JsonObject coreConfig) { this.tenantIdentifier = tenantIdentifier; this.coreConfig = coreConfig == null ? new JsonObject() : coreConfig; this.emailPasswordConfig = emailPasswordConfig; this.passwordlessConfig = passwordlessConfig; this.thirdPartyConfig = thirdPartyConfig; - this.totpConfig = totpConfig; - this.firstFactors = firstFactors; - this.defaultRequiredFactorIds = defaultRequiredFactorIds; + this.firstFactors = firstFactors == null || firstFactors.length == 0 ? null : firstFactors; + this.requiredSecondaryFactors = requiredSecondaryFactors == null || requiredSecondaryFactors.length == 0 ? null : requiredSecondaryFactors; } public TenantConfig(TenantConfig other) { @@ -83,9 +75,8 @@ public TenantConfig(TenantConfig other) { this.emailPasswordConfig = new EmailPasswordConfig(other.emailPasswordConfig.enabled); this.passwordlessConfig = new PasswordlessConfig(other.passwordlessConfig.enabled); this.thirdPartyConfig = new ThirdPartyConfig(other.thirdPartyConfig.enabled, other.thirdPartyConfig.providers.clone()); - this.totpConfig = new TotpConfig(other.totpConfig.enabled); this.firstFactors = other.firstFactors == null ? null : other.firstFactors.clone(); - this.defaultRequiredFactorIds = other.defaultRequiredFactorIds == null ? null : other.defaultRequiredFactorIds.clone(); + this.requiredSecondaryFactors = other.requiredSecondaryFactors == null ? null : other.requiredSecondaryFactors.clone(); } public boolean deepEquals(TenantConfig other) { @@ -96,9 +87,8 @@ public boolean deepEquals(TenantConfig other) { this.emailPasswordConfig.equals(other.emailPasswordConfig) && this.passwordlessConfig.equals(other.passwordlessConfig) && this.thirdPartyConfig.equals(other.thirdPartyConfig) && - this.totpConfig.equals(other.totpConfig) && Utils.unorderedArrayEquals(this.firstFactors, other.firstFactors) && // order is not important - Objects.deepEquals(this.defaultRequiredFactorIds, other.defaultRequiredFactorIds) && // order is important + Utils.unorderedArrayEquals(this.requiredSecondaryFactors, other.requiredSecondaryFactors) && // order is not important this.coreConfig.equals(other.coreConfig); } @@ -123,6 +113,14 @@ public JsonObject toJson(boolean shouldProtectDbConfig, Storage storage, String[ tenantConfigObject.add("thirdParty", this.thirdPartyConfig.toJson()); tenantConfigObject.addProperty("tenantId", this.tenantIdentifier.getTenantId()); + if (tenantConfigObject.has("firstFactors") && tenantConfigObject.get("firstFactors").getAsJsonArray().size() == 0) { + tenantConfigObject.remove("firstFactors"); + } + + if (tenantConfigObject.has("requiredSecondaryFactors") && tenantConfigObject.get("requiredSecondaryFactors").getAsJsonArray().size() == 0) { + tenantConfigObject.remove("requiredSecondaryFactors"); + } + if (shouldProtectDbConfig) { String[] protectedConfigs = storage.getProtectedConfigsFromSuperTokensSaaSUsers(); for (String config : protectedConfigs) { diff --git a/src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java b/src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java deleted file mode 100644 index 47d42349..00000000 --- a/src/main/java/io/supertokens/pluginInterface/multitenancy/TotpConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.supertokens.pluginInterface.multitenancy; - -public class TotpConfig { - public boolean enabled; - - public TotpConfig(boolean enabled) { - this.enabled = enabled; - } - - @Override - public boolean equals(Object other) { - if (other instanceof TotpConfig) { - TotpConfig otherTotpConfig = (TotpConfig) other; - return otherTotpConfig.enabled == this.enabled; - } - return false; - } -} From 93336d80c42b826f04e1b53b86040bf89ecf6e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20Lengyel?= Date: Mon, 29 Jan 2024 06:34:56 +0100 Subject: [PATCH 93/93] feat: Add support to update the signing key type of a session (#136) * feat: make refresh update the signing key type of sessions * chore: update changelog --- CHANGELOG.md | 3 +++ .../session/noSqlStorage/SessionNoSQLStorage_1.java | 2 +- .../pluginInterface/session/sqlStorage/SessionSQLStorage.java | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 504f0151..5d7a8bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Adds `createdAt` field to `TOTPDevice` - TOTPSQLStorage interface changes - Adds `getDeviceByName_Transaction` and `createDevice_Transaction` functions +- Adds a new `useStaticKey` param to `updateSessionInfo_Transaction` + - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to + change the signing key type of a session ## [4.0.5] - 2023-12-05 diff --git a/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionNoSQLStorage_1.java b/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionNoSQLStorage_1.java index 95274c5e..89c15a21 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionNoSQLStorage_1.java +++ b/src/main/java/io/supertokens/pluginInterface/session/noSqlStorage/SessionNoSQLStorage_1.java @@ -47,5 +47,5 @@ public interface SessionNoSQLStorage_1 extends SessionStorage, NoSQLStorage_1 { SessionInfoWithLastUpdated getSessionInfo_Transaction(String sessionHandle) throws StorageQueryException; boolean updateSessionInfo_Transaction(String sessionHandle, String refreshTokenHash2, long expiry, - String lastUpdatedSign) throws StorageQueryException; + String lastUpdatedSign, boolean useStaticKey) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java index d4556199..0c0436c8 100644 --- a/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/session/sqlStorage/SessionSQLStorage.java @@ -54,7 +54,7 @@ SessionInfo getSessionInfo_Transaction(TenantIdentifier tenantIdentifier, Transa void updateSessionInfo_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String sessionHandle, String refreshTokenHash2, - long expiry) throws StorageQueryException; + long expiry, boolean useStaticKey) throws StorageQueryException; void deleteSessionsOfUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException;