diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 250f9c75a5..10df66ece5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -24,6 +24,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxDevices; private long maxAssets; + private long maxCustomers; + private long maxUsers; + private long maxDashboards; + private long maxRuleChains; private String transportTenantMsgRateLimit; private String transportTenantTelemetryMsgRateLimit; diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 923baa1df4..97cdb31f5d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -332,7 +332,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ long maxAssets = profileConfiguration.getMaxAssets(); if (maxAssets > 0) { long currentAssetsCount = assetDao.countAssetsByTenantId(tenantId); - if (maxAssets >= currentAssetsCount) { + if (currentAssetsCount >= maxAssets) { throw new DataValidationException("Can't create assets more then " + maxAssets); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index 11fa5cb7cd..5ecfd2fe62 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -54,5 +54,7 @@ public interface CustomerDao extends Dao { * @return the optional customer object */ Optional findCustomersByTenantIdAndTitle(UUID tenantId, String title); + + Long countCustomersByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 14d591e7e8..b649200e23 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; @@ -28,6 +29,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; @@ -38,6 +40,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.user.UserService; @@ -75,6 +78,10 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Autowired private DashboardService dashboardService; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerById [{}]", customerId); @@ -162,6 +169,15 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override protected void validateCreate(TenantId tenantId, Customer customer) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxCustomers = profileConfiguration.getMaxCustomers(); + if (maxCustomers > 0) { + long currentCustomersCount = customerDao.countCustomersByTenantId(tenantId); + if (currentCustomersCount >= maxCustomers) { + throw new DataValidationException("Can't create customers more then " + maxCustomers); + } + } customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( c -> { throw new DataValidationException("Customer with such title already exists!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index 0429c6d469..0c013b4d33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -32,4 +32,5 @@ public interface DashboardDao extends Dao { */ Dashboard save(TenantId tenantId, Dashboard dashboard); + Long countDashboardsByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 112f998cd4..f9ec7755d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; @@ -31,12 +32,14 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.concurrent.ExecutionException; @@ -61,6 +64,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Autowired private CustomerDao customerDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardById [{}]", dashboardId); @@ -214,6 +221,19 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb private DataValidator dashboardValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, Dashboard data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxDashboards = profileConfiguration.getMaxDashboards(); + if (maxDashboards > 0) { + long currentDashboardsCount = dashboardDao.countDashboardsByTenantId(tenantId); + if (currentDashboardsCount >= maxDashboards) { + throw new DataValidationException("Can't create dashboards more then " + maxDashboards); + } + } + } + @Override protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { if (StringUtils.isEmpty(dashboard.getTitle())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 24c597b4df..5728259ad7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -532,7 +532,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe long maxDevices = profileConfiguration.getMaxDevices(); if (maxDevices > 0) { long currentDevicesCount = deviceDao.countDevicesByTenantId(tenantId); - if (maxDevices >= currentDevicesCount) { + if (currentDevicesCount >= maxDevices) { throw new DataValidationException("Can't create devices more then " + maxDevices); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 5997839119..aaa5040f6e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -24,6 +24,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; @@ -44,11 +45,13 @@ import org.thingsboard.server.common.data.rule.RuleChainData; import org.thingsboard.server.common.data.rule.RuleChainImportResult; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.ArrayList; @@ -81,6 +84,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Autowired private TenantDao tenantDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public RuleChain saveRuleChain(RuleChain ruleChain) { ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); @@ -580,6 +587,19 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private DataValidator ruleChainValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, RuleChain data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxRuleChains = profileConfiguration.getMaxRuleChains(); + if (maxRuleChains > 0) { + long currentRuleChainsCount = ruleChainDao.countRuleChainsByTenantId(tenantId); + if (currentRuleChainsCount >= maxRuleChains) { + throw new DataValidationException("Can't create rule chains more then " + maxRuleChains); + } + } + } + @Override protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { if (StringUtils.isEmpty(ruleChain.getName())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index c3214425fe..9f792200fc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.rule; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; @@ -36,4 +37,5 @@ public interface RuleChainDao extends Dao { */ PageData findRuleChainsByTenantId(UUID tenantId, PageLink pageLink); + Long countRuleChainsByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java index bf2a845ec0..4b5a8e0791 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java @@ -37,4 +37,5 @@ public interface CustomerRepository extends PagingAndSortingRepository { + + Long countByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java index 8d637f0cbe..a8cf3136ff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.model.sql.DashboardEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; @@ -43,4 +44,9 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao getCrudRepository() { return dashboardRepository; } + + @Override + public Long countDashboardsByTenantId(TenantId tenantId) { + return dashboardRepository.countByTenantId(tenantId.getId()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 57d1545a94..051d13f805 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; @@ -56,4 +57,8 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao imple DaoUtil.toPageable(pageLink))); } + + @Override + public Long countUsersByTenantId(TenantId tenantId) { + return userRepository.countByTenantId(tenantId.getId()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index 1d512a7944..ceaf09ec59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -47,4 +47,5 @@ public interface UserRepository extends PagingAndSortingRepository { * @return the list of user entities */ PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); - + + Long countUsersByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 314684c3fe..acf9c7b1b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; @@ -36,6 +37,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -43,6 +45,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.HashMap; @@ -84,6 +87,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Autowired private CustomerDao customerDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public User findUserByEmail(TenantId tenantId, String email) { log.trace("Executing findUserByEmail [{}]", email); @@ -364,6 +371,19 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic private DataValidator userValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, User data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxUsers = profileConfiguration.getMaxUsers(); + if (maxUsers > 0) { + long currentUsersCount = userDao.countUsersByTenantId(tenantId); + if (currentUsersCount >= maxUsers) { + throw new DataValidationException("Can't create users more then " + maxUsers); + } + } + } + @Override protected void validateDataImpl(TenantId requestTenantId, User user) { if (StringUtils.isEmpty(user.getEmail())) { diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 12258ffd5f..ae9fd8ea9c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -40,6 +40,54 @@ {{ 'tenant-profile.maximum-assets-range' | translate}} + + tenant-profile.maximum-customers + + + {{ 'tenant-profile.maximum-customers-required' | translate}} + + + {{ 'tenant-profile.maximum-customers-range' | translate}} + + + + tenant-profile.maximum-users + + + {{ 'tenant-profile.maximum-users-required' | translate}} + + + {{ 'tenant-profile.maximum-users-range' | translate}} + + + + tenant-profile.maximum-dashboards + + + {{ 'tenant-profile.maximum-dashboards-required' | translate}} + + + {{ 'tenant-profile.maximum-dashboards-range' | translate}} + + + + tenant-profile.maximum-rule-chains + + + {{ 'tenant-profile.maximum-rule-chains-required' | translate}} + + + {{ 'tenant-profile.maximum-rule-chains-range' | translate}} + + tenant-profile.max-transport-messages