Browse Source
[3.3.4][Edge] Refactoring validators - moved them to a separate classes. Added device profile validation in edge processorpull/6174/head
committed by
GitHub
67 changed files with 2878 additions and 1912 deletions
@ -0,0 +1,30 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.cache; |
|||
|
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
|
|||
public interface EntitiesCacheManager { |
|||
|
|||
void removeDeviceFromCacheByName(TenantId tenantId, String name); |
|||
|
|||
void removeDeviceFromCacheById(TenantId tenantId, DeviceId deviceId); |
|||
|
|||
void removeAssetFromCacheByName(TenantId tenantId, String name); |
|||
|
|||
void removeEdgeFromCacheByName(TenantId tenantId, String name); |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.cache; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.cache.Cache; |
|||
import org.springframework.cache.CacheManager; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
import static org.thingsboard.server.common.data.CacheConstants.ASSET_CACHE; |
|||
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; |
|||
import static org.thingsboard.server.common.data.CacheConstants.EDGE_CACHE; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class EntitiesCacheManagerImpl implements EntitiesCacheManager { |
|||
|
|||
private final CacheManager cacheManager; |
|||
|
|||
@Override |
|||
public void removeDeviceFromCacheByName(TenantId tenantId, String name) { |
|||
Cache cache = cacheManager.getCache(DEVICE_CACHE); |
|||
cache.evict(Arrays.asList(tenantId, name)); |
|||
} |
|||
|
|||
@Override |
|||
public void removeDeviceFromCacheById(TenantId tenantId, DeviceId deviceId) { |
|||
if (deviceId == null) { |
|||
return; |
|||
} |
|||
Cache cache = cacheManager.getCache(DEVICE_CACHE); |
|||
cache.evict(Arrays.asList(tenantId, deviceId)); |
|||
} |
|||
|
|||
@Override |
|||
public void removeAssetFromCacheByName(TenantId tenantId, String name) { |
|||
Cache cache = cacheManager.getCache(ASSET_CACHE); |
|||
cache.evict(Arrays.asList(tenantId, name)); |
|||
} |
|||
|
|||
@Override |
|||
public void removeEdgeFromCacheByName(TenantId tenantId, String name) { |
|||
Cache cache = cacheManager.getCache(EDGE_CACHE); |
|||
cache.evict(Arrays.asList(tenantId, name)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.AdminSettings; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.settings.AdminSettingsService; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class AdminSettingsDataValidator extends DataValidator<AdminSettings> { |
|||
|
|||
private final AdminSettingsService adminSettingsService; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, AdminSettings adminSettings) { |
|||
AdminSettings existentAdminSettingsWithKey = adminSettingsService.findAdminSettingsByKey(tenantId, adminSettings.getKey()); |
|||
if (existentAdminSettingsWithKey != null) { |
|||
throw new DataValidationException("Admin settings with such name already exists!"); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, AdminSettings adminSettings) { |
|||
AdminSettings existentAdminSettings = adminSettingsService.findAdminSettingsById(tenantId, adminSettings.getId()); |
|||
if (existentAdminSettings != null) { |
|||
if (!existentAdminSettings.getKey().equals(adminSettings.getKey())) { |
|||
throw new DataValidationException("Changing key of admin settings entry is prohibited!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, AdminSettings adminSettings) { |
|||
if (StringUtils.isEmpty(adminSettings.getKey())) { |
|||
throw new DataValidationException("Key should be specified!"); |
|||
} |
|||
if (adminSettings.getJsonValue() == null) { |
|||
throw new DataValidationException("Json value should be specified!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.alarm.Alarm; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class AlarmDataValidator extends DataValidator<Alarm> { |
|||
|
|||
private final TenantDao tenantDao; |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Alarm alarm) { |
|||
if (StringUtils.isEmpty(alarm.getType())) { |
|||
throw new DataValidationException("Alarm type should be specified!"); |
|||
} |
|||
if (alarm.getOriginator() == null) { |
|||
throw new DataValidationException("Alarm originator should be specified!"); |
|||
} |
|||
if (alarm.getSeverity() == null) { |
|||
throw new DataValidationException("Alarm severity should be specified!"); |
|||
} |
|||
if (alarm.getStatus() == null) { |
|||
throw new DataValidationException("Alarm status should be specified!"); |
|||
} |
|||
if (alarm.getTenantId() == null) { |
|||
throw new DataValidationException("Alarm should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(alarm.getTenantId(), alarm.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Alarm is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.ApiUsageState; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class ApiUsageDataValidator extends DataValidator<ApiUsageState> { |
|||
|
|||
private final TenantDao tenantDao; |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId requestTenantId, ApiUsageState apiUsageState) { |
|||
if (apiUsageState.getTenantId() == null) { |
|||
throw new DataValidationException("ApiUsageState should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(requestTenantId, apiUsageState.getTenantId().getId()); |
|||
if (tenant == null && !requestTenantId.equals(TenantId.SYS_TENANT_ID)) { |
|||
throw new DataValidationException("ApiUsageState is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
if (apiUsageState.getEntityId() == null) { |
|||
throw new DataValidationException("UsageRecord should be assigned to entity!"); |
|||
} else if (apiUsageState.getEntityId().getEntityType() != EntityType.TENANT && apiUsageState.getEntityId().getEntityType() != EntityType.CUSTOMER) { |
|||
throw new DataValidationException("Only Tenant and Customer Usage Records are supported!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.asset.Asset; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.asset.AssetDao; |
|||
import org.thingsboard.server.dao.asset.BaseAssetService; |
|||
import org.thingsboard.server.dao.cache.EntitiesCacheManager; |
|||
import org.thingsboard.server.dao.customer.CustomerDao; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
|||
|
|||
@Component |
|||
public class AssetDataValidator extends DataValidator<Asset> { |
|||
|
|||
@Autowired |
|||
private AssetDao assetDao; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
private CustomerDao customerDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Autowired |
|||
private EntitiesCacheManager cacheManager; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, Asset asset) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
if (!BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { |
|||
long maxAssets = profileConfiguration.getMaxAssets(); |
|||
validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, Asset asset) { |
|||
Asset old = assetDao.findById(asset.getTenantId(), asset.getId().getId()); |
|||
if (old == null) { |
|||
throw new DataValidationException("Can't update non existing asset!"); |
|||
} |
|||
if (!old.getName().equals(asset.getName())) { |
|||
cacheManager.removeAssetFromCacheByName(tenantId, old.getName()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Asset asset) { |
|||
if (StringUtils.isEmpty(asset.getType())) { |
|||
throw new DataValidationException("Asset type should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(asset.getName())) { |
|||
throw new DataValidationException("Asset name should be specified!"); |
|||
} |
|||
if (asset.getTenantId() == null) { |
|||
throw new DataValidationException("Asset should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(tenantId, asset.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Asset is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
if (asset.getCustomerId() == null) { |
|||
asset.setCustomerId(new CustomerId(NULL_UUID)); |
|||
} else if (!asset.getCustomerId().getId().equals(NULL_UUID)) { |
|||
Customer customer = customerDao.findById(tenantId, asset.getCustomerId().getId()); |
|||
if (customer == null) { |
|||
throw new DataValidationException("Can't assign asset to non-existent customer!"); |
|||
} |
|||
if (!customer.getTenantId().equals(asset.getTenantId())) { |
|||
throw new DataValidationException("Can't assign asset to customer from different tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.audit.AuditLog; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
@Component |
|||
public class AuditLogDataValidator extends DataValidator<AuditLog> { |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, AuditLog auditLog) { |
|||
if (auditLog.getEntityId() == null) { |
|||
throw new DataValidationException("Entity Id should be specified!"); |
|||
} |
|||
if (auditLog.getTenantId() == null) { |
|||
throw new DataValidationException("Tenant Id should be specified!"); |
|||
} |
|||
if (auditLog.getUserId() == null) { |
|||
throw new DataValidationException("User Id should be specified!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.thingsboard.server.common.data.BaseData; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.OtaPackageInfo; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.dao.device.DeviceProfileDao; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
import java.util.Objects; |
|||
|
|||
public abstract class BaseOtaPackageDataValidator<D extends BaseData<?>> extends DataValidator<D> { |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
private DeviceProfileDao deviceProfileDao; |
|||
|
|||
protected void validateImpl(OtaPackageInfo otaPackageInfo) { |
|||
if (otaPackageInfo.getTenantId() == null) { |
|||
throw new DataValidationException("OtaPackage should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("OtaPackage is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
|
|||
if (otaPackageInfo.getDeviceProfileId() != null) { |
|||
DeviceProfile deviceProfile = deviceProfileDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getDeviceProfileId().getId()); |
|||
if (deviceProfile == null) { |
|||
throw new DataValidationException("OtaPackage is referencing to non-existent device profile!"); |
|||
} |
|||
} |
|||
|
|||
if (otaPackageInfo.getType() == null) { |
|||
throw new DataValidationException("Type should be specified!"); |
|||
} |
|||
|
|||
if (StringUtils.isEmpty(otaPackageInfo.getTitle())) { |
|||
throw new DataValidationException("OtaPackage title should be specified!"); |
|||
} |
|||
|
|||
if (StringUtils.isEmpty(otaPackageInfo.getVersion())) { |
|||
throw new DataValidationException("OtaPackage version should be specified!"); |
|||
} |
|||
|
|||
if (otaPackageInfo.getTitle().length() > 255) { |
|||
throw new DataValidationException("The length of title should be equal or shorter than 255"); |
|||
} |
|||
|
|||
if (otaPackageInfo.getVersion().length() > 255) { |
|||
throw new DataValidationException("The length of version should be equal or shorter than 255"); |
|||
} |
|||
} |
|||
|
|||
protected void validateUpdate(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { |
|||
if (!otaPackageOld.getType().equals(otaPackage.getType())) { |
|||
throw new DataValidationException("Updating type is prohibited!"); |
|||
} |
|||
|
|||
if (!otaPackageOld.getTitle().equals(otaPackage.getTitle())) { |
|||
throw new DataValidationException("Updating otaPackage title is prohibited!"); |
|||
} |
|||
|
|||
if (!otaPackageOld.getVersion().equals(otaPackage.getVersion())) { |
|||
throw new DataValidationException("Updating otaPackage version is prohibited!"); |
|||
} |
|||
|
|||
if (!Objects.equals(otaPackage.getTag(), otaPackageOld.getTag())) { |
|||
throw new DataValidationException("Updating otaPackage tag is prohibited!"); |
|||
} |
|||
|
|||
if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { |
|||
throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!"); |
|||
} |
|||
|
|||
if (otaPackageOld.getFileName() != null && !otaPackageOld.getFileName().equals(otaPackage.getFileName())) { |
|||
throw new DataValidationException("Updating otaPackage file name is prohibited!"); |
|||
} |
|||
|
|||
if (otaPackageOld.getContentType() != null && !otaPackageOld.getContentType().equals(otaPackage.getContentType())) { |
|||
throw new DataValidationException("Updating otaPackage content type is prohibited!"); |
|||
} |
|||
|
|||
if (otaPackageOld.getChecksumAlgorithm() != null && !otaPackageOld.getChecksumAlgorithm().equals(otaPackage.getChecksumAlgorithm())) { |
|||
throw new DataValidationException("Updating otaPackage content type is prohibited!"); |
|||
} |
|||
|
|||
if (otaPackageOld.getChecksum() != null && !otaPackageOld.getChecksum().equals(otaPackage.getChecksum())) { |
|||
throw new DataValidationException("Updating otaPackage content type is prohibited!"); |
|||
} |
|||
|
|||
if (otaPackageOld.getDataSize() != null && !otaPackageOld.getDataSize().equals(otaPackage.getDataSize())) { |
|||
throw new DataValidationException("Updating otaPackage data size is prohibited!"); |
|||
} |
|||
|
|||
if (otaPackageOld.getUrl() != null && !otaPackageOld.getUrl().equals(otaPackage.getUrl())) { |
|||
throw new DataValidationException("Updating otaPackage URL is prohibited!"); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
@Component |
|||
public class ClientRegistrationTemplateDataValidator extends DataValidator<OAuth2ClientRegistrationTemplate> { |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) { |
|||
throw new DataValidationException("Provider ID should be specified!"); |
|||
} |
|||
if (clientRegistrationTemplate.getMapperConfig() == null) { |
|||
throw new DataValidationException("Mapper config should be specified!"); |
|||
} |
|||
if (clientRegistrationTemplate.getMapperConfig().getType() == null) { |
|||
throw new DataValidationException("Mapper type should be specified!"); |
|||
} |
|||
if (clientRegistrationTemplate.getMapperConfig().getBasic() == null) { |
|||
throw new DataValidationException("Basic mapper config should be specified!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
@Component |
|||
public class ComponentDescriptorDataValidator extends DataValidator<ComponentDescriptor> { |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) { |
|||
if (plugin.getType() == null) { |
|||
throw new DataValidationException("Component type should be specified!"); |
|||
} |
|||
if (plugin.getScope() == null) { |
|||
throw new DataValidationException("Component scope should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(plugin.getName())) { |
|||
throw new DataValidationException("Component name should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(plugin.getClazz())) { |
|||
throw new DataValidationException("Component clazz should be specified!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.customer.CustomerDao; |
|||
import org.thingsboard.server.dao.customer.CustomerServiceImpl; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
@Component |
|||
public class CustomerDataValidator extends DataValidator<Customer> { |
|||
|
|||
@Autowired |
|||
private CustomerDao customerDao; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, Customer customer) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxCustomers = profileConfiguration.getMaxCustomers(); |
|||
|
|||
validateNumberOfEntitiesPerTenant(tenantId, customerDao, maxCustomers, EntityType.CUSTOMER); |
|||
customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( |
|||
c -> { |
|||
throw new DataValidationException("Customer with such title already exists!"); |
|||
} |
|||
); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, Customer customer) { |
|||
customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( |
|||
c -> { |
|||
if (!c.getId().equals(customer.getId())) { |
|||
throw new DataValidationException("Customer with such title already exists!"); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Customer customer) { |
|||
if (StringUtils.isEmpty(customer.getTitle())) { |
|||
throw new DataValidationException("Customer title should be specified!"); |
|||
} |
|||
if (customer.getTitle().equals(CustomerServiceImpl.PUBLIC_CUSTOMER_TITLE)) { |
|||
throw new DataValidationException("'Public' title for customer is system reserved!"); |
|||
} |
|||
if (!StringUtils.isEmpty(customer.getEmail())) { |
|||
validateEmail(customer.getEmail()); |
|||
} |
|||
if (customer.getTenantId() == null) { |
|||
throw new DataValidationException("Customer should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(tenantId, customer.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Customer is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.dashboard.DashboardDao; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
@Component |
|||
public class DashboardDataValidator extends DataValidator<Dashboard> { |
|||
|
|||
@Autowired |
|||
private DashboardDao dashboardDao; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, Dashboard data) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxDashboards = profileConfiguration.getMaxDashboards(); |
|||
validateNumberOfEntitiesPerTenant(tenantId, dashboardDao, maxDashboards, EntityType.DASHBOARD); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { |
|||
if (StringUtils.isEmpty(dashboard.getTitle())) { |
|||
throw new DataValidationException("Dashboard title should be specified!"); |
|||
} |
|||
if (dashboard.getTenantId() == null) { |
|||
throw new DataValidationException("Dashboard should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(tenantId, dashboard.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.dao.device.DeviceCredentialsDao; |
|||
import org.thingsboard.server.dao.device.DeviceService; |
|||
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
@Component |
|||
public class DeviceCredentialsDataValidator extends DataValidator<DeviceCredentials> { |
|||
|
|||
@Autowired |
|||
private DeviceCredentialsDao deviceCredentialsDao; |
|||
|
|||
@Autowired |
|||
private DeviceService deviceService; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) { |
|||
if (deviceCredentialsDao.findByDeviceId(tenantId, deviceCredentials.getDeviceId().getId()) != null) { |
|||
throw new DeviceCredentialsValidationException("Credentials for this device are already specified!"); |
|||
} |
|||
if (deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()) != null) { |
|||
throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { |
|||
if (deviceCredentialsDao.findById(tenantId, deviceCredentials.getUuidId()) == null) { |
|||
throw new DeviceCredentialsValidationException("Unable to update non-existent device credentials!"); |
|||
} |
|||
DeviceCredentials existingCredentials = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()); |
|||
if (existingCredentials != null && !existingCredentials.getId().equals(deviceCredentials.getId())) { |
|||
throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, DeviceCredentials deviceCredentials) { |
|||
if (deviceCredentials.getDeviceId() == null) { |
|||
throw new DeviceCredentialsValidationException("Device credentials should be assigned to device!"); |
|||
} |
|||
if (deviceCredentials.getCredentialsType() == null) { |
|||
throw new DeviceCredentialsValidationException("Device credentials type should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) { |
|||
throw new DeviceCredentialsValidationException("Device credentials id should be specified!"); |
|||
} |
|||
Device device = deviceService.findDeviceById(tenantId, deviceCredentials.getDeviceId()); |
|||
if (device == null) { |
|||
throw new DeviceCredentialsValidationException("Can't assign device credentials to non-existent device!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,147 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.OtaPackage; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.ota.OtaPackageType; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.cache.EntitiesCacheManager; |
|||
import org.thingsboard.server.dao.customer.CustomerDao; |
|||
import org.thingsboard.server.dao.device.DeviceDao; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.ota.OtaPackageService; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
|||
|
|||
@Component |
|||
public class DeviceDataValidator extends DataValidator<Device> { |
|||
|
|||
@Autowired |
|||
private DeviceDao deviceDao; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
private CustomerDao customerDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Autowired |
|||
private OtaPackageService otaPackageService; |
|||
|
|||
@Autowired |
|||
private EntitiesCacheManager cacheManager; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, Device device) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxDevices = profileConfiguration.getMaxDevices(); |
|||
validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, Device device) { |
|||
Device old = deviceDao.findById(device.getTenantId(), device.getId().getId()); |
|||
if (old == null) { |
|||
throw new DataValidationException("Can't update non existing device!"); |
|||
} |
|||
if (!old.getName().equals(device.getName())) { |
|||
cacheManager.removeDeviceFromCacheByName(tenantId, old.getName()); |
|||
cacheManager.removeDeviceFromCacheById(tenantId, device.getId()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Device device) { |
|||
if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) { |
|||
throw new DataValidationException("Device name should be specified!"); |
|||
} |
|||
if (device.getTenantId() == null) { |
|||
throw new DataValidationException("Device should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(device.getTenantId(), device.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Device is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
if (device.getCustomerId() == null) { |
|||
device.setCustomerId(new CustomerId(NULL_UUID)); |
|||
} else if (!device.getCustomerId().getId().equals(NULL_UUID)) { |
|||
Customer customer = customerDao.findById(device.getTenantId(), device.getCustomerId().getId()); |
|||
if (customer == null) { |
|||
throw new DataValidationException("Can't assign device to non-existent customer!"); |
|||
} |
|||
if (!customer.getTenantId().getId().equals(device.getTenantId().getId())) { |
|||
throw new DataValidationException("Can't assign device to customer from different tenant!"); |
|||
} |
|||
} |
|||
Optional.ofNullable(device.getDeviceData()) |
|||
.flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration())) |
|||
.ifPresent(DeviceTransportConfiguration::validate); |
|||
|
|||
if (device.getFirmwareId() != null) { |
|||
OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, device.getFirmwareId()); |
|||
if (firmware == null) { |
|||
throw new DataValidationException("Can't assign non-existent firmware!"); |
|||
} |
|||
if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { |
|||
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); |
|||
} |
|||
if (firmware.getData() == null && !firmware.hasUrl()) { |
|||
throw new DataValidationException("Can't assign firmware with empty data!"); |
|||
} |
|||
if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) { |
|||
throw new DataValidationException("Can't assign firmware with different deviceProfile!"); |
|||
} |
|||
} |
|||
|
|||
if (device.getSoftwareId() != null) { |
|||
OtaPackage software = otaPackageService.findOtaPackageById(tenantId, device.getSoftwareId()); |
|||
if (software == null) { |
|||
throw new DataValidationException("Can't assign non-existent software!"); |
|||
} |
|||
if (!software.getType().equals(OtaPackageType.SOFTWARE)) { |
|||
throw new DataValidationException("Can't assign software with type: " + software.getType()); |
|||
} |
|||
if (software.getData() == null && !software.hasUrl()) { |
|||
throw new DataValidationException("Can't assign software with empty data!"); |
|||
} |
|||
if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) { |
|||
throw new DataValidationException("Can't assign firmware with different deviceProfile!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,524 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import com.google.protobuf.Descriptors; |
|||
import com.google.protobuf.DynamicMessage; |
|||
import com.squareup.wire.Syntax; |
|||
import com.squareup.wire.schema.Field; |
|||
import com.squareup.wire.schema.Location; |
|||
import com.squareup.wire.schema.internal.parser.EnumElement; |
|||
import com.squareup.wire.schema.internal.parser.FieldElement; |
|||
import com.squareup.wire.schema.internal.parser.MessageElement; |
|||
import com.squareup.wire.schema.internal.parser.OneOfElement; |
|||
import com.squareup.wire.schema.internal.parser.ProtoFileElement; |
|||
import com.squareup.wire.schema.internal.parser.ProtoParser; |
|||
import com.squareup.wire.schema.internal.parser.TypeElement; |
|||
import org.eclipse.leshan.core.util.SecurityUtil; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.CollectionUtils; |
|||
import org.thingsboard.server.common.data.DashboardInfo; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
|||
import org.thingsboard.server.common.data.OtaPackage; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; |
|||
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
|||
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential; |
|||
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; |
|||
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.RPKLwM2MBootstrapServerCredential; |
|||
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.X509LwM2MBootstrapServerCredential; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.ota.OtaPackageType; |
|||
import org.thingsboard.server.common.data.rule.RuleChain; |
|||
import org.thingsboard.server.common.msg.EncryptionUtil; |
|||
import org.thingsboard.server.common.msg.queue.ServiceType; |
|||
import org.thingsboard.server.dao.dashboard.DashboardService; |
|||
import org.thingsboard.server.dao.device.DeviceDao; |
|||
import org.thingsboard.server.dao.device.DeviceProfileDao; |
|||
import org.thingsboard.server.dao.device.DeviceProfileService; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; |
|||
import org.thingsboard.server.dao.ota.OtaPackageService; |
|||
import org.thingsboard.server.dao.rule.RuleChainService; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
import org.thingsboard.server.queue.QueueService; |
|||
|
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Component |
|||
public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> { |
|||
|
|||
private static final Location LOCATION = new Location("", "", -1, -1); |
|||
private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; |
|||
private static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; |
|||
private static final String RPC_REQUEST_PROTO_SCHEMA = "rpc request proto schema"; |
|||
private static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema"; |
|||
@Autowired |
|||
private DeviceProfileDao deviceProfileDao; |
|||
@Autowired |
|||
@Lazy |
|||
private DeviceProfileService deviceProfileService; |
|||
@Autowired |
|||
private DeviceDao deviceDao; |
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
@Autowired |
|||
@Lazy |
|||
private QueueService queueService; |
|||
@Autowired |
|||
private OtaPackageService otaPackageService; |
|||
@Autowired |
|||
private RuleChainService ruleChainService; |
|||
@Autowired |
|||
private DashboardService dashboardService; |
|||
|
|||
private static String invalidSchemaProvidedMessage(String schemaName) { |
|||
return "[Transport Configuration] invalid " + schemaName + " provided!"; |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, DeviceProfile deviceProfile) { |
|||
if (org.thingsboard.server.common.data.StringUtils.isEmpty(deviceProfile.getName())) { |
|||
throw new DataValidationException("Device profile name should be specified!"); |
|||
} |
|||
if (deviceProfile.getType() == null) { |
|||
throw new DataValidationException("Device profile type should be specified!"); |
|||
} |
|||
if (deviceProfile.getTransportType() == null) { |
|||
throw new DataValidationException("Device profile transport type should be specified!"); |
|||
} |
|||
if (deviceProfile.getTenantId() == null) { |
|||
throw new DataValidationException("Device profile should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(deviceProfile.getTenantId(), deviceProfile.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Device profile is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
if (deviceProfile.isDefault()) { |
|||
DeviceProfile defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId); |
|||
if (defaultDeviceProfile != null && !defaultDeviceProfile.getId().equals(deviceProfile.getId())) { |
|||
throw new DataValidationException("Another default device profile is present in scope of current tenant!"); |
|||
} |
|||
} |
|||
if (!org.thingsboard.server.common.data.StringUtils.isEmpty(deviceProfile.getDefaultQueueName()) && queueService != null) { |
|||
if (!queueService.getQueuesByServiceType(ServiceType.TB_RULE_ENGINE).contains(deviceProfile.getDefaultQueueName())) { |
|||
throw new DataValidationException("Device profile is referencing to non-existent queue!"); |
|||
} |
|||
} |
|||
if (deviceProfile.getProvisionType() == null) { |
|||
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); |
|||
} |
|||
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); |
|||
transportConfiguration.validate(); |
|||
if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) { |
|||
MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; |
|||
if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) { |
|||
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = |
|||
(ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); |
|||
validateProtoSchemas(protoTransportPayloadConfiguration); |
|||
validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); |
|||
validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); |
|||
} |
|||
} else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { |
|||
CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; |
|||
CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); |
|||
if (coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration) { |
|||
DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; |
|||
TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); |
|||
if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) { |
|||
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; |
|||
validateProtoSchemas(protoTransportPayloadConfiguration); |
|||
validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); |
|||
validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); |
|||
} |
|||
} |
|||
} else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) { |
|||
List<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap(); |
|||
if (lwM2MBootstrapServersConfigurations != null) { |
|||
validateLwm2mServersConfigOfBootstrapForClient(lwM2MBootstrapServersConfigurations, |
|||
((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).isBootstrapServerUpdateEnable()); |
|||
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) { |
|||
validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential); |
|||
} |
|||
} |
|||
} |
|||
|
|||
List<DeviceProfileAlarm> profileAlarms = deviceProfile.getProfileData().getAlarms(); |
|||
|
|||
if (!CollectionUtils.isEmpty(profileAlarms)) { |
|||
Set<String> alarmTypes = new HashSet<>(); |
|||
for (DeviceProfileAlarm alarm : profileAlarms) { |
|||
String alarmType = alarm.getAlarmType(); |
|||
if (StringUtils.isEmpty(alarmType)) { |
|||
throw new DataValidationException("Alarm rule type should be specified!"); |
|||
} |
|||
if (!alarmTypes.add(alarmType)) { |
|||
throw new DataValidationException(String.format("Can't create device profile with the same alarm rule types: \"%s\"!", alarmType)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (deviceProfile.getDefaultRuleChainId() != null) { |
|||
RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, deviceProfile.getDefaultRuleChainId()); |
|||
if (ruleChain == null) { |
|||
throw new DataValidationException("Can't assign non-existent rule chain!"); |
|||
} |
|||
} |
|||
|
|||
if (deviceProfile.getDefaultDashboardId() != null) { |
|||
DashboardInfo dashboard = dashboardService.findDashboardInfoById(tenantId, deviceProfile.getDefaultDashboardId()); |
|||
if (dashboard == null) { |
|||
throw new DataValidationException("Can't assign non-existent dashboard!"); |
|||
} |
|||
} |
|||
|
|||
if (deviceProfile.getFirmwareId() != null) { |
|||
OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getFirmwareId()); |
|||
if (firmware == null) { |
|||
throw new DataValidationException("Can't assign non-existent firmware!"); |
|||
} |
|||
if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { |
|||
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); |
|||
} |
|||
if (firmware.getData() == null && !firmware.hasUrl()) { |
|||
throw new DataValidationException("Can't assign firmware with empty data!"); |
|||
} |
|||
if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) { |
|||
throw new DataValidationException("Can't assign firmware with different deviceProfile!"); |
|||
} |
|||
} |
|||
|
|||
if (deviceProfile.getSoftwareId() != null) { |
|||
OtaPackage software = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getSoftwareId()); |
|||
if (software == null) { |
|||
throw new DataValidationException("Can't assign non-existent software!"); |
|||
} |
|||
if (!software.getType().equals(OtaPackageType.SOFTWARE)) { |
|||
throw new DataValidationException("Can't assign software with type: " + software.getType()); |
|||
} |
|||
if (software.getData() == null && !software.hasUrl()) { |
|||
throw new DataValidationException("Can't assign software with empty data!"); |
|||
} |
|||
if (!software.getDeviceProfileId().equals(deviceProfile.getId())) { |
|||
throw new DataValidationException("Can't assign firmware with different deviceProfile!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, DeviceProfile deviceProfile) { |
|||
DeviceProfile old = deviceProfileDao.findById(deviceProfile.getTenantId(), deviceProfile.getId().getId()); |
|||
if (old == null) { |
|||
throw new DataValidationException("Can't update non existing device profile!"); |
|||
} |
|||
boolean profileTypeChanged = !old.getType().equals(deviceProfile.getType()); |
|||
boolean transportTypeChanged = !old.getTransportType().equals(deviceProfile.getTransportType()); |
|||
if (profileTypeChanged || transportTypeChanged) { |
|||
Long profileDeviceCount = deviceDao.countDevicesByDeviceProfileId(deviceProfile.getTenantId(), deviceProfile.getId().getId()); |
|||
if (profileDeviceCount > 0) { |
|||
String message = null; |
|||
if (profileTypeChanged) { |
|||
message = "Can't change device profile type because devices referenced it!"; |
|||
} else if (transportTypeChanged) { |
|||
message = "Can't change device profile transport type because devices referenced it!"; |
|||
} |
|||
throw new DataValidationException(message); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void validateProtoSchemas(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { |
|||
try { |
|||
validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA); |
|||
validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA); |
|||
validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema(), RPC_REQUEST_PROTO_SCHEMA); |
|||
validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcResponseProtoSchema(), RPC_RESPONSE_PROTO_SCHEMA); |
|||
} catch (Exception exception) { |
|||
throw new DataValidationException(exception.getMessage()); |
|||
} |
|||
} |
|||
|
|||
private void validateTransportProtoSchema(String schema, String schemaName) throws IllegalArgumentException { |
|||
ProtoParser schemaParser = new ProtoParser(LOCATION, schema.toCharArray()); |
|||
ProtoFileElement protoFileElement; |
|||
try { |
|||
protoFileElement = schemaParser.readProtoFile(); |
|||
} catch (Exception e) { |
|||
throw new IllegalArgumentException("[Transport Configuration] failed to parse " + schemaName + " due to: " + e.getMessage()); |
|||
} |
|||
checkProtoFileSyntax(schemaName, protoFileElement); |
|||
checkProtoFileCommonSettings(schemaName, protoFileElement.getOptions().isEmpty(), " Schema options don't support!"); |
|||
checkProtoFileCommonSettings(schemaName, protoFileElement.getPublicImports().isEmpty(), " Schema public imports don't support!"); |
|||
checkProtoFileCommonSettings(schemaName, protoFileElement.getImports().isEmpty(), " Schema imports don't support!"); |
|||
checkProtoFileCommonSettings(schemaName, protoFileElement.getExtendDeclarations().isEmpty(), " Schema extend declarations don't support!"); |
|||
checkTypeElements(schemaName, protoFileElement); |
|||
} |
|||
|
|||
private void checkProtoFileSyntax(String schemaName, ProtoFileElement protoFileElement) { |
|||
if (protoFileElement.getSyntax() == null || !protoFileElement.getSyntax().equals(Syntax.PROTO_3)) { |
|||
throw new IllegalArgumentException("[Transport Configuration] invalid schema syntax: " + protoFileElement.getSyntax() + |
|||
" for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!"); |
|||
} |
|||
} |
|||
|
|||
private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) { |
|||
if (!isEmptySettings) { |
|||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage); |
|||
} |
|||
} |
|||
|
|||
private void checkTypeElements(String schemaName, ProtoFileElement protoFileElement) { |
|||
List<TypeElement> types = protoFileElement.getTypes(); |
|||
if (!types.isEmpty()) { |
|||
if (types.stream().noneMatch(typeElement -> typeElement instanceof MessageElement)) { |
|||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " At least one Message definition should exists!"); |
|||
} else { |
|||
checkEnumElements(schemaName, getEnumElements(types)); |
|||
checkMessageElements(schemaName, getMessageTypes(types)); |
|||
} |
|||
} else { |
|||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Type elements is empty!"); |
|||
} |
|||
} |
|||
|
|||
private void checkFieldElements(String schemaName, List<FieldElement> fieldElements) { |
|||
if (!fieldElements.isEmpty()) { |
|||
boolean hasRequiredLabel = fieldElements.stream().anyMatch(fieldElement -> { |
|||
Field.Label label = fieldElement.getLabel(); |
|||
return label != null && label.equals(Field.Label.REQUIRED); |
|||
}); |
|||
if (hasRequiredLabel) { |
|||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Required labels are not supported!"); |
|||
} |
|||
boolean hasDefaultValue = fieldElements.stream().anyMatch(fieldElement -> fieldElement.getDefaultValue() != null); |
|||
if (hasDefaultValue) { |
|||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Default values are not supported!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void checkEnumElements(String schemaName, List<EnumElement> enumTypes) { |
|||
if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getNestedTypes().isEmpty())) { |
|||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Nested types in Enum definitions are not supported!"); |
|||
} |
|||
if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getOptions().isEmpty())) { |
|||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Enum definitions options are not supported!"); |
|||
} |
|||
} |
|||
|
|||
private void checkMessageElements(String schemaName, List<MessageElement> messageElementsList) { |
|||
if (!messageElementsList.isEmpty()) { |
|||
messageElementsList.forEach(messageElement -> { |
|||
checkProtoFileCommonSettings(schemaName, messageElement.getGroups().isEmpty(), |
|||
" Message definition groups don't support!"); |
|||
checkProtoFileCommonSettings(schemaName, messageElement.getOptions().isEmpty(), |
|||
" Message definition options don't support!"); |
|||
checkProtoFileCommonSettings(schemaName, messageElement.getExtensions().isEmpty(), |
|||
" Message definition extensions don't support!"); |
|||
checkProtoFileCommonSettings(schemaName, messageElement.getReserveds().isEmpty(), |
|||
" Message definition reserved elements don't support!"); |
|||
checkFieldElements(schemaName, messageElement.getFields()); |
|||
List<OneOfElement> oneOfs = messageElement.getOneOfs(); |
|||
if (!oneOfs.isEmpty()) { |
|||
oneOfs.forEach(oneOfElement -> { |
|||
checkProtoFileCommonSettings(schemaName, oneOfElement.getGroups().isEmpty(), |
|||
" OneOf definition groups don't support!"); |
|||
checkFieldElements(schemaName, oneOfElement.getFields()); |
|||
}); |
|||
} |
|||
List<TypeElement> nestedTypes = messageElement.getNestedTypes(); |
|||
if (!nestedTypes.isEmpty()) { |
|||
List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes); |
|||
if (!nestedEnumTypes.isEmpty()) { |
|||
checkEnumElements(schemaName, nestedEnumTypes); |
|||
} |
|||
List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes); |
|||
checkMessageElements(schemaName, nestedMessageTypes); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
private List<MessageElement> getMessageTypes(List<TypeElement> types) { |
|||
return types.stream() |
|||
.filter(typeElement -> typeElement instanceof MessageElement) |
|||
.map(typeElement -> (MessageElement) typeElement) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
private List<EnumElement> getEnumElements(List<TypeElement> types) { |
|||
return types.stream() |
|||
.filter(typeElement -> typeElement instanceof EnumElement) |
|||
.map(typeElement -> (EnumElement) typeElement) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
private void validateTelemetryDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { |
|||
String deviceTelemetryProtoSchema = protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(); |
|||
Descriptors.Descriptor telemetryDynamicMessageDescriptor = protoTransportPayloadTypeConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema); |
|||
if (telemetryDynamicMessageDescriptor == null) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Failed to get telemetryDynamicMessageDescriptor!"); |
|||
} else { |
|||
List<Descriptors.FieldDescriptor> fields = telemetryDynamicMessageDescriptor.getFields(); |
|||
if (CollectionUtils.isEmpty(fields)) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " " + telemetryDynamicMessageDescriptor.getName() + " fields is empty!"); |
|||
} else if (fields.size() == 2) { |
|||
Descriptors.FieldDescriptor tsFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("ts"); |
|||
Descriptors.FieldDescriptor valuesFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("values"); |
|||
if (tsFieldDescriptor != null && valuesFieldDescriptor != null) { |
|||
if (!Descriptors.FieldDescriptor.Type.MESSAGE.equals(valuesFieldDescriptor.getType())) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'values' has invalid data type. Only message type is supported!"); |
|||
} |
|||
if (!Descriptors.FieldDescriptor.Type.INT64.equals(tsFieldDescriptor.getType())) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid data type. Only int64 type is supported!"); |
|||
} |
|||
if (!tsFieldDescriptor.hasOptionalKeyword()) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void validateRpcRequestDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { |
|||
DynamicMessage.Builder rpcRequestDynamicMessageBuilder = protoTransportPayloadTypeConfiguration.getRpcRequestDynamicMessageBuilder(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema()); |
|||
Descriptors.Descriptor rpcRequestDynamicMessageDescriptor = rpcRequestDynamicMessageBuilder.getDescriptorForType(); |
|||
if (rpcRequestDynamicMessageDescriptor == null) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get rpcRequestDynamicMessageDescriptor!"); |
|||
} else { |
|||
if (CollectionUtils.isEmpty(rpcRequestDynamicMessageDescriptor.getFields()) || rpcRequestDynamicMessageDescriptor.getFields().size() != 3) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " " + rpcRequestDynamicMessageDescriptor.getName() + " message should always contains 3 fields: method, requestId and params!"); |
|||
} |
|||
Descriptors.FieldDescriptor methodFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("method"); |
|||
if (methodFieldDescriptor == null) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: method!"); |
|||
} else { |
|||
if (!Descriptors.FieldDescriptor.Type.STRING.equals(methodFieldDescriptor.getType())) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid data type. Only string type is supported!"); |
|||
} |
|||
if (methodFieldDescriptor.isRepeated()) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid label!"); |
|||
} |
|||
} |
|||
Descriptors.FieldDescriptor requestIdFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("requestId"); |
|||
if (requestIdFieldDescriptor == null) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: requestId!"); |
|||
} else { |
|||
if (!Descriptors.FieldDescriptor.Type.INT32.equals(requestIdFieldDescriptor.getType())) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid data type. Only int32 type is supported!"); |
|||
} |
|||
if (requestIdFieldDescriptor.isRepeated()) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid label!"); |
|||
} |
|||
} |
|||
Descriptors.FieldDescriptor paramsFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("params"); |
|||
if (paramsFieldDescriptor == null) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: params!"); |
|||
} else { |
|||
if (paramsFieldDescriptor.isRepeated()) { |
|||
throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'params' has invalid label!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void validateLwm2mServersConfigOfBootstrapForClient(List<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations, boolean isBootstrapServerUpdateEnable) { |
|||
Set<String> uris = new HashSet<>(); |
|||
Set<Integer> shortServerIds = new HashSet<>(); |
|||
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) { |
|||
AbstractLwM2MBootstrapServerCredential serverConfig = (AbstractLwM2MBootstrapServerCredential) bootstrapServerCredential; |
|||
if (!isBootstrapServerUpdateEnable && serverConfig.isBootstrapServerIs()) { |
|||
throw new DeviceCredentialsValidationException("Bootstrap config must not include \"Bootstrap Server\". \"Include Bootstrap Server updates\" is " + isBootstrapServerUpdateEnable + "."); |
|||
} |
|||
String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server" + " shortServerId: " + serverConfig.getShortServerId() + ":"; |
|||
if (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) { |
|||
throw new DeviceCredentialsValidationException(server + " ShortServerId must not be less than 1 and more than 65534!"); |
|||
} |
|||
if (!shortServerIds.add(serverConfig.getShortServerId())) { |
|||
throw new DeviceCredentialsValidationException(server + " \"Short server Id\" value = " + serverConfig.getShortServerId() + ". This value must be a unique value for all servers!"); |
|||
} |
|||
String uri = serverConfig.getHost() + ":" + serverConfig.getPort(); |
|||
if (!uris.add(uri)) { |
|||
throw new DeviceCredentialsValidationException(server + " \"Host + port\" value = " + uri + ". This value must be a unique value for all servers!"); |
|||
} |
|||
Integer port; |
|||
if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) { |
|||
port = serverConfig.isBootstrapServerIs() ? 5687 : 5685; |
|||
} else { |
|||
port = serverConfig.isBootstrapServerIs() ? 5688 : 5686; |
|||
} |
|||
if (serverConfig.getPort() == null || serverConfig.getPort().intValue() != port) { |
|||
String errMsg = server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!"; |
|||
throw new DeviceCredentialsValidationException(errMsg); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void validateLwm2mServersCredentialOfBootstrapForClient(LwM2MBootstrapServerCredential bootstrapServerConfig) { |
|||
String server; |
|||
switch (bootstrapServerConfig.getSecurityMode()) { |
|||
case NO_SEC: |
|||
case PSK: |
|||
break; |
|||
case RPK: |
|||
RPKLwM2MBootstrapServerCredential rpkServerCredentials = (RPKLwM2MBootstrapServerCredential) bootstrapServerConfig; |
|||
server = rpkServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; |
|||
if (StringUtils.isEmpty(rpkServerCredentials.getServerPublicKey())) { |
|||
throw new DeviceCredentialsValidationException(server + " RPK public key must be specified!"); |
|||
} |
|||
try { |
|||
String pubkRpkSever = EncryptionUtil.pubkTrimNewLines(rpkServerCredentials.getServerPublicKey()); |
|||
rpkServerCredentials.setServerPublicKey(pubkRpkSever); |
|||
SecurityUtil.publicKey.decode(rpkServerCredentials.getDecodedCServerPublicKey()); |
|||
} catch (Exception e) { |
|||
throw new DeviceCredentialsValidationException(server + " RPK public key must be in standard [RFC7250] and then encoded to Base64 format!"); |
|||
} |
|||
break; |
|||
case X509: |
|||
X509LwM2MBootstrapServerCredential x509ServerCredentials = (X509LwM2MBootstrapServerCredential) bootstrapServerConfig; |
|||
server = x509ServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; |
|||
if (StringUtils.isEmpty(x509ServerCredentials.getServerPublicKey())) { |
|||
throw new DeviceCredentialsValidationException(server + " X509 certificate must be specified!"); |
|||
} |
|||
|
|||
try { |
|||
String certServer = EncryptionUtil.certTrimNewLines(x509ServerCredentials.getServerPublicKey()); |
|||
x509ServerCredentials.setServerPublicKey(certServer); |
|||
SecurityUtil.certificate.decode(x509ServerCredentials.getDecodedCServerPublicKey()); |
|||
} catch (Exception e) { |
|||
throw new DeviceCredentialsValidationException(server + " X509 certificate must be in DER-encoded X509v3 format and support only EC algorithm and then encoded to Base64 format!"); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.edge.Edge; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.cache.EntitiesCacheManager; |
|||
import org.thingsboard.server.dao.customer.CustomerDao; |
|||
import org.thingsboard.server.dao.edge.EdgeDao; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class EdgeDataValidator extends DataValidator<Edge> { |
|||
|
|||
private final EdgeDao edgeDao; |
|||
private final TenantDao tenantDao; |
|||
private final CustomerDao customerDao; |
|||
private final EntitiesCacheManager cacheManager; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, Edge edge) { |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, Edge edge) { |
|||
Edge old = edgeDao.findById(edge.getTenantId(), edge.getId().getId()); |
|||
if (!old.getName().equals(edge.getName())) { |
|||
cacheManager.removeEdgeFromCacheByName(tenantId, old.getName()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Edge edge) { |
|||
if (org.springframework.util.StringUtils.isEmpty(edge.getType())) { |
|||
throw new DataValidationException("Edge type should be specified!"); |
|||
} |
|||
if (org.springframework.util.StringUtils.isEmpty(edge.getName())) { |
|||
throw new DataValidationException("Edge name should be specified!"); |
|||
} |
|||
if (org.springframework.util.StringUtils.isEmpty(edge.getSecret())) { |
|||
throw new DataValidationException("Edge secret should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(edge.getRoutingKey())) { |
|||
throw new DataValidationException("Edge routing key should be specified!"); |
|||
} |
|||
if (edge.getTenantId() == null) { |
|||
throw new DataValidationException("Edge should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(edge.getTenantId(), edge.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Edge is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
if (edge.getCustomerId() == null) { |
|||
edge.setCustomerId(new CustomerId(NULL_UUID)); |
|||
} else if (!edge.getCustomerId().getId().equals(NULL_UUID)) { |
|||
Customer customer = customerDao.findById(edge.getTenantId(), edge.getCustomerId().getId()); |
|||
if (customer == null) { |
|||
throw new DataValidationException("Can't assign edge to non-existent customer!"); |
|||
} |
|||
if (!customer.getTenantId().getId().equals(edge.getTenantId().getId())) { |
|||
throw new DataValidationException("Can't assign edge to customer from different tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.edge.EdgeEvent; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
@Component |
|||
public class EdgeEventDataValidator extends DataValidator<EdgeEvent> { |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, EdgeEvent edgeEvent) { |
|||
if (edgeEvent.getEdgeId() == null) { |
|||
throw new DataValidationException("Edge id should be specified!"); |
|||
} |
|||
if (edgeEvent.getAction() == null) { |
|||
throw new DataValidationException("Edge Event action should be specified!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.EntityView; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.customer.CustomerDao; |
|||
import org.thingsboard.server.dao.entityview.EntityViewDao; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class EntityViewDataValidator extends DataValidator<EntityView> { |
|||
|
|||
private final EntityViewDao entityViewDao; |
|||
private final TenantDao tenantDao; |
|||
private final CustomerDao customerDao; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, EntityView entityView) { |
|||
entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) |
|||
.ifPresent(e -> { |
|||
throw new DataValidationException("Entity view with such name already exists!"); |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, EntityView entityView) { |
|||
entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) |
|||
.ifPresent(e -> { |
|||
if (!e.getUuidId().equals(entityView.getUuidId())) { |
|||
throw new DataValidationException("Entity view with such name already exists!"); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, EntityView entityView) { |
|||
if (StringUtils.isEmpty(entityView.getType())) { |
|||
throw new DataValidationException("Entity View type should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(entityView.getName())) { |
|||
throw new DataValidationException("Entity view name should be specified!"); |
|||
} |
|||
if (entityView.getTenantId() == null) { |
|||
throw new DataValidationException("Entity view should be assigned to tenant!"); |
|||
} else { |
|||
Tenant tenant = tenantDao.findById(tenantId, entityView.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Entity view is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
if (entityView.getCustomerId() == null) { |
|||
entityView.setCustomerId(new CustomerId(NULL_UUID)); |
|||
} else if (!entityView.getCustomerId().getId().equals(NULL_UUID)) { |
|||
Customer customer = customerDao.findById(tenantId, entityView.getCustomerId().getId()); |
|||
if (customer == null) { |
|||
throw new DataValidationException("Can't assign entity view to non-existent customer!"); |
|||
} |
|||
if (!customer.getTenantId().getId().equals(entityView.getTenantId().getId())) { |
|||
throw new DataValidationException("Can't assign entity view to customer from different tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Event; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
@Component |
|||
public class EventDataValidator extends DataValidator<Event> { |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Event event) { |
|||
if (event.getEntityId() == null) { |
|||
throw new DataValidationException("Entity id should be specified!."); |
|||
} |
|||
if (StringUtils.isEmpty(event.getType())) { |
|||
throw new DataValidationException("Event type should be specified!."); |
|||
} |
|||
if (event.getBody() == null) { |
|||
throw new DataValidationException("Event body should be specified!."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,105 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.OtaPackage; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.ota.OtaPackageDao; |
|||
import org.thingsboard.server.dao.ota.OtaPackageService; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
|
|||
import static org.thingsboard.server.common.data.EntityType.OTA_PACKAGE; |
|||
|
|||
@Component |
|||
public class OtaPackageDataValidator extends BaseOtaPackageDataValidator<OtaPackage> { |
|||
|
|||
@Autowired |
|||
private OtaPackageDao otaPackageDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private OtaPackageService otaPackageService; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, OtaPackage otaPackage) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); |
|||
validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { |
|||
validateImpl(otaPackage); |
|||
|
|||
if (!otaPackage.hasUrl()) { |
|||
if (StringUtils.isEmpty(otaPackage.getFileName())) { |
|||
throw new DataValidationException("OtaPackage file name should be specified!"); |
|||
} |
|||
|
|||
if (StringUtils.isEmpty(otaPackage.getContentType())) { |
|||
throw new DataValidationException("OtaPackage content type should be specified!"); |
|||
} |
|||
|
|||
if (otaPackage.getChecksumAlgorithm() == null) { |
|||
throw new DataValidationException("OtaPackage checksum algorithm should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(otaPackage.getChecksum())) { |
|||
throw new DataValidationException("OtaPackage checksum should be specified!"); |
|||
} |
|||
|
|||
String currentChecksum; |
|||
|
|||
currentChecksum = otaPackageService.generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); |
|||
|
|||
if (!currentChecksum.equals(otaPackage.getChecksum())) { |
|||
throw new DataValidationException("Wrong otaPackage file!"); |
|||
} |
|||
} else { |
|||
if (otaPackage.getData() != null) { |
|||
throw new DataValidationException("File can't be saved if URL present!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, OtaPackage otaPackage) { |
|||
OtaPackage otaPackageOld = otaPackageDao.findById(tenantId, otaPackage.getUuidId()); |
|||
|
|||
validateUpdate(otaPackage, otaPackageOld); |
|||
|
|||
if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { |
|||
throw new DataValidationException("Updating otaPackage data is prohibited!"); |
|||
} |
|||
|
|||
if (otaPackageOld.getData() == null && otaPackage.getData() != null) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); |
|||
validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.OtaPackageInfo; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.ota.OtaPackageInfoDao; |
|||
|
|||
@Component |
|||
public class OtaPackageInfoDataValidator extends BaseOtaPackageDataValidator<OtaPackageInfo> { |
|||
|
|||
@Autowired |
|||
private OtaPackageInfoDao otaPackageInfoDao; |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, OtaPackageInfo otaPackageInfo) { |
|||
validateImpl(otaPackageInfo); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, OtaPackageInfo otaPackage) { |
|||
OtaPackageInfo otaPackageOld = otaPackageInfoDao.findById(tenantId, otaPackage.getUuidId()); |
|||
validateUpdate(otaPackage, otaPackageOld); |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.resource.TbResourceDao; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
import static org.thingsboard.server.common.data.EntityType.TB_RESOURCE; |
|||
|
|||
@Component |
|||
public class ResourceDataValidator extends DataValidator<TbResource> { |
|||
|
|||
@Autowired |
|||
private TbResourceDao resourceDao; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, TbResource resource) { |
|||
if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId)) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); |
|||
validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length(), TB_RESOURCE); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, TbResource resource) { |
|||
if (StringUtils.isEmpty(resource.getTitle())) { |
|||
throw new DataValidationException("Resource title should be specified!"); |
|||
} |
|||
if (resource.getResourceType() == null) { |
|||
throw new DataValidationException("Resource type should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(resource.getFileName())) { |
|||
throw new DataValidationException("Resource file name should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(resource.getResourceKey())) { |
|||
throw new DataValidationException("Resource key should be specified!"); |
|||
} |
|||
if (resource.getTenantId() == null) { |
|||
resource.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); |
|||
} |
|||
if (!resource.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { |
|||
Tenant tenant = tenantDao.findById(tenantId, resource.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Resource is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.rule.RuleChain; |
|||
import org.thingsboard.server.common.data.rule.RuleChainType; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.rule.RuleChainDao; |
|||
import org.thingsboard.server.dao.rule.RuleChainService; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
|
|||
@Component |
|||
public class RuleChainDataValidator extends DataValidator<RuleChain> { |
|||
|
|||
@Autowired |
|||
private RuleChainDao ruleChainDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private RuleChainService ruleChainService; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, RuleChain data) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxRuleChains = profileConfiguration.getMaxRuleChains(); |
|||
validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { |
|||
if (StringUtils.isEmpty(ruleChain.getName())) { |
|||
throw new DataValidationException("Rule chain name should be specified!"); |
|||
} |
|||
if (ruleChain.getType() == null) { |
|||
ruleChain.setType(RuleChainType.CORE); |
|||
} |
|||
if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { |
|||
throw new DataValidationException("Rule chain should be assigned to tenant!"); |
|||
} |
|||
Tenant tenant = tenantDao.findById(tenantId, ruleChain.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Rule chain is referencing to non-existent tenant!"); |
|||
} |
|||
if (ruleChain.isRoot() && RuleChainType.CORE.equals(ruleChain.getType())) { |
|||
RuleChain rootRuleChain = ruleChainService.getRootTenantRuleChain(ruleChain.getTenantId()); |
|||
if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) { |
|||
throw new DataValidationException("Another root rule chain is present in scope of current tenant!"); |
|||
} |
|||
} |
|||
if (ruleChain.isRoot() && RuleChainType.EDGE.equals(ruleChain.getType())) { |
|||
RuleChain edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(ruleChain.getTenantId()); |
|||
if (edgeTemplateRootRuleChain != null && !edgeTemplateRootRuleChain.getId().equals(ruleChain.getId())) { |
|||
throw new DataValidationException("Another edge template root rule chain is present in scope of current tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.TenantProfile; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
import org.thingsboard.server.dao.tenant.TenantProfileService; |
|||
|
|||
@Component |
|||
public class TenantDataValidator extends DataValidator<Tenant> { |
|||
|
|||
@Value("${zk.enabled}") |
|||
private Boolean zkEnabled; |
|||
|
|||
@Autowired |
|||
private TenantProfileService tenantProfileService; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, Tenant tenant) { |
|||
if (StringUtils.isEmpty(tenant.getTitle())) { |
|||
throw new DataValidationException("Tenant title should be specified!"); |
|||
} |
|||
if (!StringUtils.isEmpty(tenant.getEmail())) { |
|||
validateEmail(tenant.getEmail()); |
|||
} |
|||
validateTenantProfile(tenantId, tenant); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, Tenant tenant) { |
|||
Tenant old = tenantDao.findById(TenantId.SYS_TENANT_ID, tenantId.getId()); |
|||
if (old == null) { |
|||
throw new DataValidationException("Can't update non existing tenant!"); |
|||
} |
|||
validateTenantProfile(tenantId, tenant); |
|||
} |
|||
|
|||
private void validateTenantProfile(TenantId tenantId, Tenant tenant) { |
|||
TenantProfile tenantProfileById = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); |
|||
if (!zkEnabled && (tenantProfileById.isIsolatedTbCore() || tenantProfileById.isIsolatedTbRuleEngine())) { |
|||
throw new DataValidationException("Can't use isolated tenant profiles in monolith setup!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.TenantProfile; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantProfileDao; |
|||
import org.thingsboard.server.dao.tenant.TenantProfileService; |
|||
|
|||
@Component |
|||
public class TenantProfileDataValidator extends DataValidator<TenantProfile> { |
|||
|
|||
@Autowired |
|||
private TenantProfileDao tenantProfileDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TenantProfileService tenantProfileService; |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { |
|||
if (StringUtils.isEmpty(tenantProfile.getName())) { |
|||
throw new DataValidationException("Tenant profile name should be specified!"); |
|||
} |
|||
if (tenantProfile.getProfileData() == null) { |
|||
throw new DataValidationException("Tenant profile data should be specified!"); |
|||
} |
|||
if (tenantProfile.getProfileData().getConfiguration() == null) { |
|||
throw new DataValidationException("Tenant profile data configuration should be specified!"); |
|||
} |
|||
if (tenantProfile.isDefault()) { |
|||
TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId); |
|||
if (defaultTenantProfile != null && !defaultTenantProfile.getId().equals(tenantProfile.getId())) { |
|||
throw new DataValidationException("Another default tenant profile is present!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, TenantProfile tenantProfile) { |
|||
TenantProfile old = tenantProfileDao.findById(TenantId.SYS_TENANT_ID, tenantProfile.getId().getId()); |
|||
if (old == null) { |
|||
throw new DataValidationException("Can't update non existing tenant profile!"); |
|||
} else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) { |
|||
throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); |
|||
} else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) { |
|||
throw new DataValidationException("Can't update isolatedTbCore property!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.security.UserCredentials; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.exception.IncorrectParameterException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.user.UserCredentialsDao; |
|||
import org.thingsboard.server.dao.user.UserService; |
|||
|
|||
@Component |
|||
public class UserCredentialsDataValidator extends DataValidator<UserCredentials> { |
|||
|
|||
@Autowired |
|||
private UserCredentialsDao userCredentialsDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private UserService userService; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, UserCredentials userCredentials) { |
|||
throw new IncorrectParameterException("Creation of new user credentials is prohibited."); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, UserCredentials userCredentials) { |
|||
if (userCredentials.getUserId() == null) { |
|||
throw new DataValidationException("User credentials should be assigned to user!"); |
|||
} |
|||
if (userCredentials.isEnabled()) { |
|||
if (StringUtils.isEmpty(userCredentials.getPassword())) { |
|||
throw new DataValidationException("Enabled user credentials should have password!"); |
|||
} |
|||
if (StringUtils.isNotEmpty(userCredentials.getActivateToken())) { |
|||
throw new DataValidationException("Enabled user credentials can't have activate token!"); |
|||
} |
|||
} |
|||
UserCredentials existingUserCredentialsEntity = userCredentialsDao.findById(tenantId, userCredentials.getId().getId()); |
|||
if (existingUserCredentialsEntity == null) { |
|||
throw new DataValidationException("Unable to update non-existent user credentials!"); |
|||
} |
|||
User user = userService.findUserById(tenantId, userCredentials.getUserId()); |
|||
if (user == null) { |
|||
throw new DataValidationException("Can't assign user credentials to non-existent user!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
|||
import org.thingsboard.server.dao.customer.CustomerDao; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
import org.thingsboard.server.dao.user.UserDao; |
|||
import org.thingsboard.server.dao.user.UserService; |
|||
|
|||
@Component |
|||
public class UserDataValidator extends DataValidator<User> { |
|||
|
|||
@Autowired |
|||
private UserDao userDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private UserService userService; |
|||
|
|||
@Autowired |
|||
private TenantDao tenantDao; |
|||
|
|||
@Autowired |
|||
private CustomerDao customerDao; |
|||
|
|||
@Autowired |
|||
@Lazy |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, User user) { |
|||
if (!user.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { |
|||
DefaultTenantProfileConfiguration profileConfiguration = |
|||
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
|||
long maxUsers = profileConfiguration.getMaxUsers(); |
|||
validateNumberOfEntitiesPerTenant(tenantId, userDao, maxUsers, EntityType.USER); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId requestTenantId, User user) { |
|||
if (StringUtils.isEmpty(user.getEmail())) { |
|||
throw new DataValidationException("User email should be specified!"); |
|||
} |
|||
|
|||
validateEmail(user.getEmail()); |
|||
|
|||
Authority authority = user.getAuthority(); |
|||
if (authority == null) { |
|||
throw new DataValidationException("User authority isn't defined!"); |
|||
} |
|||
TenantId tenantId = user.getTenantId(); |
|||
if (tenantId == null) { |
|||
tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); |
|||
user.setTenantId(tenantId); |
|||
} |
|||
CustomerId customerId = user.getCustomerId(); |
|||
if (customerId == null) { |
|||
customerId = new CustomerId(ModelConstants.NULL_UUID); |
|||
user.setCustomerId(customerId); |
|||
} |
|||
|
|||
switch (authority) { |
|||
case SYS_ADMIN: |
|||
if (!tenantId.getId().equals(ModelConstants.NULL_UUID) |
|||
|| !customerId.getId().equals(ModelConstants.NULL_UUID)) { |
|||
throw new DataValidationException("System administrator can't be assigned neither to tenant nor to customer!"); |
|||
} |
|||
break; |
|||
case TENANT_ADMIN: |
|||
if (tenantId.getId().equals(ModelConstants.NULL_UUID)) { |
|||
throw new DataValidationException("Tenant administrator should be assigned to tenant!"); |
|||
} else if (!customerId.getId().equals(ModelConstants.NULL_UUID)) { |
|||
throw new DataValidationException("Tenant administrator can't be assigned to customer!"); |
|||
} |
|||
break; |
|||
case CUSTOMER_USER: |
|||
if (tenantId.getId().equals(ModelConstants.NULL_UUID) |
|||
|| customerId.getId().equals(ModelConstants.NULL_UUID)) { |
|||
throw new DataValidationException("Customer user should be assigned to customer!"); |
|||
} |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
User existentUserWithEmail = userService.findUserByEmail(tenantId, user.getEmail()); |
|||
if (existentUserWithEmail != null && !isSameData(existentUserWithEmail, user)) { |
|||
throw new DataValidationException("User with email '" + user.getEmail() + "' " |
|||
+ " already present in database!"); |
|||
} |
|||
if (!tenantId.getId().equals(ModelConstants.NULL_UUID)) { |
|||
Tenant tenant = tenantDao.findById(tenantId, user.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("User is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
if (!customerId.getId().equals(ModelConstants.NULL_UUID)) { |
|||
Customer customer = customerDao.findById(tenantId, user.getCustomerId().getId()); |
|||
if (customer == null) { |
|||
throw new DataValidationException("User is referencing to non-existent customer!"); |
|||
} else if (!customer.getTenantId().getId().equals(tenantId.getId())) { |
|||
throw new DataValidationException("User can't be assigned to customer from different tenant!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.widget.WidgetType; |
|||
import org.thingsboard.server.common.data.widget.WidgetTypeDetails; |
|||
import org.thingsboard.server.common.data.widget.WidgetsBundle; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
import org.thingsboard.server.dao.widget.WidgetTypeDao; |
|||
import org.thingsboard.server.dao.widget.WidgetsBundleDao; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class WidgetTypeDataValidator extends DataValidator<WidgetTypeDetails> { |
|||
|
|||
private final WidgetTypeDao widgetTypeDao; |
|||
private final TenantDao tenantDao; |
|||
private final WidgetsBundleDao widgetsBundleDao; |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { |
|||
if (StringUtils.isEmpty(widgetTypeDetails.getName())) { |
|||
throw new DataValidationException("Widgets type name should be specified!"); |
|||
} |
|||
if (StringUtils.isEmpty(widgetTypeDetails.getBundleAlias())) { |
|||
throw new DataValidationException("Widgets type bundle alias should be specified!"); |
|||
} |
|||
if (widgetTypeDetails.getDescriptor() == null || widgetTypeDetails.getDescriptor().size() == 0) { |
|||
throw new DataValidationException("Widgets type descriptor can't be empty!"); |
|||
} |
|||
if (widgetTypeDetails.getTenantId() == null) { |
|||
widgetTypeDetails.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); |
|||
} |
|||
if (!widgetTypeDetails.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { |
|||
Tenant tenant = tenantDao.findById(tenantId, widgetTypeDetails.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Widget type is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { |
|||
WidgetsBundle widgetsBundle = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias()); |
|||
if (widgetsBundle == null) { |
|||
throw new DataValidationException("Widget type is referencing to non-existent widgets bundle!"); |
|||
} |
|||
String alias = widgetTypeDetails.getAlias(); |
|||
if (alias == null || alias.trim().isEmpty()) { |
|||
alias = widgetTypeDetails.getName().toLowerCase().replaceAll("\\W+", "_"); |
|||
} |
|||
String originalAlias = alias; |
|||
int c = 1; |
|||
WidgetType withSameAlias; |
|||
do { |
|||
withSameAlias = widgetTypeDao.findByTenantIdBundleAliasAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias(), alias); |
|||
if (withSameAlias != null) { |
|||
alias = originalAlias + (++c); |
|||
} |
|||
} while (withSameAlias != null); |
|||
widgetTypeDetails.setAlias(alias); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { |
|||
WidgetType storedWidgetType = widgetTypeDao.findById(tenantId, widgetTypeDetails.getId().getId()); |
|||
if (!storedWidgetType.getTenantId().getId().equals(widgetTypeDetails.getTenantId().getId())) { |
|||
throw new DataValidationException("Can't move existing widget type to different tenant!"); |
|||
} |
|||
if (!storedWidgetType.getBundleAlias().equals(widgetTypeDetails.getBundleAlias())) { |
|||
throw new DataValidationException("Update of widget type bundle alias is prohibited!"); |
|||
} |
|||
if (!storedWidgetType.getAlias().equals(widgetTypeDetails.getAlias())) { |
|||
throw new DataValidationException("Update of widget type alias is prohibited!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* 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 org.thingsboard.server.dao.service.validator; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.widget.WidgetsBundle; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
import org.thingsboard.server.dao.tenant.TenantDao; |
|||
import org.thingsboard.server.dao.widget.WidgetsBundleDao; |
|||
|
|||
@Component |
|||
@AllArgsConstructor |
|||
public class WidgetsBundleDataValidator extends DataValidator<WidgetsBundle> { |
|||
|
|||
private final WidgetsBundleDao widgetsBundleDao; |
|||
private final TenantDao tenantDao; |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, WidgetsBundle widgetsBundle) { |
|||
if (StringUtils.isEmpty(widgetsBundle.getTitle())) { |
|||
throw new DataValidationException("Widgets bundle title should be specified!"); |
|||
} |
|||
if (widgetsBundle.getTenantId() == null) { |
|||
widgetsBundle.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); |
|||
} |
|||
if (!widgetsBundle.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { |
|||
Tenant tenant = tenantDao.findById(tenantId, widgetsBundle.getTenantId().getId()); |
|||
if (tenant == null) { |
|||
throw new DataValidationException("Widgets bundle is referencing to non-existent tenant!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, WidgetsBundle widgetsBundle) { |
|||
String alias = widgetsBundle.getAlias(); |
|||
if (alias == null || alias.trim().isEmpty()) { |
|||
alias = widgetsBundle.getTitle().toLowerCase().replaceAll("\\W+", "_"); |
|||
} |
|||
String originalAlias = alias; |
|||
int c = 1; |
|||
WidgetsBundle withSameAlias; |
|||
do { |
|||
withSameAlias = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetsBundle.getTenantId().getId(), alias); |
|||
if (withSameAlias != null) { |
|||
alias = originalAlias + (++c); |
|||
} |
|||
} while (withSameAlias != null); |
|||
widgetsBundle.setAlias(alias); |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, WidgetsBundle widgetsBundle) { |
|||
WidgetsBundle storedWidgetsBundle = widgetsBundleDao.findById(tenantId, widgetsBundle.getId().getId()); |
|||
if (!storedWidgetsBundle.getTenantId().getId().equals(widgetsBundle.getTenantId().getId())) { |
|||
throw new DataValidationException("Can't move existing widgets bundle to different tenant!"); |
|||
} |
|||
if (!storedWidgetsBundle.getAlias().equals(widgetsBundle.getAlias())) { |
|||
throw new DataValidationException("Update of widgets bundle alias is prohibited!"); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue