From 95f41810ac347e782baf7e00d3ad90dc3be27e22 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 29 Mar 2022 11:32:27 +0300 Subject: [PATCH] Store 2FA account config is UserCredentials' additionalInfo --- .../DefaultTwoFactorAuthConfigManager.java | 44 ++++++++++++------- .../server/dao/user/UserService.java | 14 +++--- .../common/data/security/UserCredentials.java | 9 +++- .../dao/model/sql/UserCredentialsEntity.java | 11 +++++ .../main/resources/sql/schema-entities.sql | 3 +- 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFactorAuthConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFactorAuthConfigManager.java index bd486a6af0..ebdd03d734 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFactorAuthConfigManager.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFactorAuthConfigManager.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.security.auth.mfa.config; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; @@ -22,13 +23,13 @@ import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.settings.AdminSettingsDao; @@ -41,6 +42,7 @@ import org.thingsboard.server.service.security.auth.mfa.provider.TwoFactorAuthPr import java.util.Collections; import java.util.Optional; import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; @Service @RequiredArgsConstructor @@ -62,9 +64,8 @@ public class DefaultTwoFactorAuthConfigManager implements TwoFactorAuthConfigMan @Override public Optional getTwoFaAccountConfig(TenantId tenantId, UserId userId) { - User user = userService.findUserById(tenantId, userId); - return Optional.ofNullable(user.getAdditionalInfo()) - .flatMap(additionalInfo -> Optional.ofNullable(additionalInfo.get(TWO_FACTOR_AUTH_ACCOUNT_CONFIG_KEY)).filter(jsonNode -> !jsonNode.isNull())) + return Optional.ofNullable(getAccountInfo(tenantId, userId).get(TWO_FACTOR_AUTH_ACCOUNT_CONFIG_KEY)) + .filter(JsonNode::isObject) .map(jsonNode -> JacksonUtil.treeToValue(jsonNode, TwoFactorAuthAccountConfig.class)) .filter(twoFactorAuthAccountConfig -> { return getTwoFaProviderConfig(tenantId, twoFactorAuthAccountConfig.getProviderType()).isPresent(); @@ -76,24 +77,33 @@ public class DefaultTwoFactorAuthConfigManager implements TwoFactorAuthConfigMan getTwoFaProviderConfig(tenantId, accountConfig.getProviderType()) .orElseThrow(() -> new ThingsboardException("2FA provider is not configured", ThingsboardErrorCode.BAD_REQUEST_PARAMS)); - User user = userService.findUserById(tenantId, userId); - ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(user.getAdditionalInfo()) - .orElseGet(JacksonUtil::newObjectNode); - additionalInfo.set(TWO_FACTOR_AUTH_ACCOUNT_CONFIG_KEY, JacksonUtil.valueToTree(accountConfig)); - user.setAdditionalInfo(additionalInfo); - - userService.saveUser(user); + updateAccountInfo(tenantId, userId, accountInfo -> { + accountInfo.set(TWO_FACTOR_AUTH_ACCOUNT_CONFIG_KEY, JacksonUtil.valueToTree(accountConfig)); + }); } @Override public void deleteTwoFaAccountConfig(TenantId tenantId, UserId userId) { - User user = userService.findUserById(tenantId, userId); - ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(user.getAdditionalInfo()) - .orElseGet(JacksonUtil::newObjectNode); - additionalInfo.remove(TWO_FACTOR_AUTH_ACCOUNT_CONFIG_KEY); - user.setAdditionalInfo(additionalInfo); + updateAccountInfo(tenantId, userId, accountInfo -> { + accountInfo.remove(TWO_FACTOR_AUTH_ACCOUNT_CONFIG_KEY); + }); + } - userService.saveUser(user); + private ObjectNode getAccountInfo(TenantId tenantId, UserId userId) { + return (ObjectNode) Optional.ofNullable(userService.findUserCredentialsByUserId(tenantId, userId).getAdditionalInfo()) + .filter(JsonNode::isObject) + .orElseGet(JacksonUtil::newObjectNode); + } + + // FIXME [viacheslav]: upgrade script for credentials' additional info + private void updateAccountInfo(TenantId tenantId, UserId userId, Consumer updater) { + UserCredentials credentials = userService.findUserCredentialsByUserId(tenantId, userId); + ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(credentials.getAdditionalInfo()) + .filter(JsonNode::isObject) + .orElseGet(JacksonUtil::newObjectNode); + updater.accept(additionalInfo); + credentials.setAdditionalInfo(additionalInfo); + userService.saveUserCredentials(tenantId, credentials); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index d5d29efa40..93da1ecd6c 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -51,24 +51,24 @@ public interface UserService { UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials); - void deleteUser(TenantId tenantId, UserId userId); + void deleteUser(TenantId tenantId, UserId userId); PageData findUsersByTenantId(TenantId tenantId, PageLink pageLink); PageData findTenantAdmins(TenantId tenantId, PageLink pageLink); - void deleteTenantAdmins(TenantId tenantId); + void deleteTenantAdmins(TenantId tenantId); PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); - void deleteCustomerUsers(TenantId tenantId, CustomerId customerId); + void deleteCustomerUsers(TenantId tenantId, CustomerId customerId); - void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled); + void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled); - void resetFailedLoginAttempts(TenantId tenantId, UserId userId); + void resetFailedLoginAttempts(TenantId tenantId, UserId userId); - int increaseFailedLoginAttempts(TenantId tenantId, UserId userId); + int increaseFailedLoginAttempts(TenantId tenantId, UserId userId); - void setLastLoginTs(TenantId tenantId, UserId userId); + void setLastLoginTs(TenantId tenantId, UserId userId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java index 3816d27131..9f8dab575b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java @@ -16,12 +16,12 @@ package org.thingsboard.server.common.data.security; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; @EqualsAndHashCode(callSuper = true) -public class UserCredentials extends BaseData { +public class UserCredentials extends SearchTextBasedWithAdditionalInfo { private static final long serialVersionUID = -2108436378880529163L; @@ -87,6 +87,11 @@ public class UserCredentials extends BaseData { public void setResetToken(String resetToken) { this.resetToken = resetToken; } + + @Override + public String getSearchText() { + return null; + } @Override public String toString() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java index 4379af14e7..211977122a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java @@ -15,14 +15,18 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; @@ -31,6 +35,7 @@ import java.util.UUID; @Data @EqualsAndHashCode(callSuper = true) +@TypeDef(name = "json", typeClass = JsonStringType.class) @Entity @Table(name = ModelConstants.USER_CREDENTIALS_COLUMN_FAMILY_NAME) public final class UserCredentialsEntity extends BaseSqlEntity implements BaseEntity { @@ -50,6 +55,10 @@ public final class UserCredentialsEntity extends BaseSqlEntity @Column(name = ModelConstants.USER_CREDENTIALS_RESET_TOKEN_PROPERTY, unique = true) private String resetToken; + @Type(type = "json") + @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) + private JsonNode additionalInfo; + public UserCredentialsEntity() { super(); } @@ -66,6 +75,7 @@ public final class UserCredentialsEntity extends BaseSqlEntity this.password = userCredentials.getPassword(); this.activateToken = userCredentials.getActivateToken(); this.resetToken = userCredentials.getResetToken(); + this.additionalInfo = userCredentials.getAdditionalInfo(); } @Override @@ -79,6 +89,7 @@ public final class UserCredentialsEntity extends BaseSqlEntity userCredentials.setPassword(password); userCredentials.setActivateToken(activateToken); userCredentials.setResetToken(resetToken); + userCredentials.setAdditionalInfo(additionalInfo); return userCredentials; } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 4e2596485b..34bee652e2 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -370,7 +370,8 @@ CREATE TABLE IF NOT EXISTS user_credentials ( enabled boolean, password varchar(255), reset_token varchar(255) UNIQUE, - user_id uuid UNIQUE + user_id uuid UNIQUE, + additional_info varchar ); CREATE TABLE IF NOT EXISTS widget_type (