diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 108ae3fae1..590138dbd4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -152,6 +152,9 @@ public abstract class BaseController { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!"; + protected static final String DEFAULT_DASHBOARD = "defaultDashboardId"; + protected static final String HOME_DASHBOARD = "homeDashboardId"; + private static final ObjectMapper json = new ObjectMapper(); @Autowired @@ -1035,4 +1038,14 @@ public abstract class BaseController { } } } + + protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { + String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; + if(dashboardId != null && !dashboardId.equals("null")) { + if(dashboardService.findDashboardById(getTenantId(), new DashboardId(UUID.fromString(dashboardId))) == null) { + additionalInfo.remove(requiredFields); + } + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index 5c6fcae2f5..484b15f076 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -59,7 +59,11 @@ public class CustomerController extends BaseController { checkParameter(CUSTOMER_ID, strCustomerId); try { CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - return checkCustomerId(customerId, Operation.READ); + Customer customer = checkCustomerId(customerId, Operation.READ); + if(!customer.getAdditionalInfo().isNull()) { + processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD); + } + return customer; } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java index d05cf2179a..7621d05908 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -59,7 +60,11 @@ public class TenantController extends BaseController { checkParameter("tenantId", strTenantId); try { TenantId tenantId = new TenantId(toUUID(strTenantId)); - return checkTenantId(tenantId, Operation.READ); + Tenant tenant = checkTenantId(tenantId, Operation.READ); + if(!tenant.getAdditionalInfo().isNull()) { + processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD); + } + return tenant; } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index 393ca46779..2a1ec5810e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -92,7 +92,12 @@ public class UserController extends BaseController { checkParameter(USER_ID, strUserId); try { UserId userId = new UserId(toUUID(strUserId)); - return checkUserId(userId, Operation.READ); + User user = checkUserId(userId, Operation.READ); + if(!user.getAdditionalInfo().isNull()) { + processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD); + processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD); + } + return user; } catch (Exception e) { throw handleException(e); } @@ -339,4 +344,5 @@ public class UserController extends BaseController { throw handleException(e); } } + } diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java index ce6a781d19..5057893d5c 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java @@ -217,23 +217,19 @@ public class LwM2MModelsRepository { switch (mode) { case NO_SEC: bsServ.setHost(contextBootStrap.getBootstrapHost()); - bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk()); + bsServ.setPort(contextBootStrap.getBootstrapPortNoSec()); bsServ.setServerPublicKey(""); break; case PSK: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk()); + bsServ.setHost(contextBootStrap.getBootstrapHostSecurity()); + bsServ.setPort(contextBootStrap.getBootstrapPortSecurity()); bsServ.setServerPublicKey(""); break; case RPK: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk()); - bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); - break; case X509: - bsServ.setHost(contextBootStrap.getBootstrapSecureHost()); - bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509()); - bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias())); + bsServ.setHost(contextBootStrap.getBootstrapHostSecurity()); + bsServ.setPort(contextBootStrap.getBootstrapPortSecurity()); + bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); break; default: break; @@ -243,23 +239,19 @@ public class LwM2MModelsRepository { switch (mode) { case NO_SEC: bsServ.setHost(contextServer.getServerHost()); - bsServ.setPort(contextServer.getServerPortNoSecPsk()); + bsServ.setPort(contextServer.getServerPortNoSec()); bsServ.setServerPublicKey(""); break; case PSK: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortPsk()); + bsServ.setHost(contextServer.getServerHostSecurity()); + bsServ.setPort(contextServer.getServerPortSecurity()); bsServ.setServerPublicKey(""); break; case RPK: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortRpk()); - bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); - break; case X509: - bsServ.setHost(contextServer.getServerSecureHost()); - bsServ.setPort(contextServer.getServerPortX509()); - bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias())); + bsServ.setHost(contextServer.getServerHostSecurity()); + bsServ.setPort(contextServer.getServerPortSecurity()); + bsServ.setServerPublicKey(getPublicKey (contextServer.getServerAlias(), this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY())); break; default: break; @@ -268,6 +260,11 @@ public class LwM2MModelsRepository { return bsServ; } + private String getPublicKey (String alias, String publicServerX, String publicServerY) { + String publicKey = getServerPublicKeyX509(alias); + return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY); + } + /** * @param alias * @return PublicKey format HexString or null diff --git a/application/src/main/resources/logback.xml b/application/src/main/resources/logback.xml index d5124d5847..0d1f431a59 100644 --- a/application/src/main/resources/logback.xml +++ b/application/src/main/resources/logback.xml @@ -34,6 +34,9 @@ + + + @@ -43,4 +46,4 @@ - \ No newline at end of file + diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ddbe6eb522..4c516e48f2 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -594,14 +594,14 @@ transport: # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}" model_path_file: "${LWM2M_MODEL_PATH_FILE:}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" - recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:false}" + recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}" request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}" registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" secure: - # Only Certificate_x509: + # Certificate_x509: # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format # Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}" @@ -610,48 +610,38 @@ transport: key_store_path_file: "${KEY_STORE_PATH_FILE:}" key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}" root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" - enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}" + enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}" server: id: "${LWM2M_SERVER_ID:123}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}" - bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}" - bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}" + bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}" secure: - bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - start_psk: "${START_SERVER_PSK:true}" - start_rpk: "${START_SERVER_RPK:true}" - start_x509: "${START_SERVER_X509:true}" - bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}" - bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}" - bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}" - # Only RPK: Public & Private Key -# create_rpk: "${CREATE_RPK:}" - public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}" - public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}" - private_s: "${LWM2M_SERVER_PRIVATE_S:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}" - # Only Certificate_x509: + bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}" + bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}" + # create_rpk: "${CREATE_RPK:}" + # Only for RPK: Public & Private Key. If the keystore file is missing or not working + # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d0301070342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b] + # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b], + # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)] + public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}" + public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" + private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" # Only Certificate_x509: alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" bootstrap: - enable: "${BOOTSTRAP:true}" + enable: "${LWM2M_BOOTSTRAP_ENABLED:true}" id: "${LWM2M_SERVER_ID:111}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}" - bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}" - bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}" + bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}" secure: - bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - start_psk: "${START_SERVER_PSK_BS:true}" - start_rpk: "${START_SERVER_RPK_BS:true}" - start_x509: "${START_SERVER_X509_BS:true}" - bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}" - bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}" - bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}" - # Only RPK: Public & Private Key - public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}" - public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}" - private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}" - # Only Certificate_x509: + bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" + bind_port_security: "${LWM2M_BIND_PORT_SEC_BS:5688}" + # Only for RPK: Public & Private Key. If the keystore file is missing or not working + # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)] + # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d030107034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34] + # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34], + public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}" + public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" + private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" # Only Certificate_x509: alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}" # Redis redis_url: "${LWM2M_REDIS_URL:''}" @@ -722,6 +712,10 @@ queue: transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}" + consumer-stats: + enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" + print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" + kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" aws_sqs: use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java index a154b83c88..6f6a7746b7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java @@ -17,19 +17,25 @@ package org.thingsboard.server.common.data.query; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; -import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.io.Serializable; @Data +@RequiredArgsConstructor public class DynamicValue implements Serializable { @JsonIgnore private T resolvedValue; - @Getter private final DynamicValueSourceType sourceType; - @Getter private final String sourceAttribute; + private final boolean inherit; + + public DynamicValue(DynamicValueSourceType sourceType, String sourceAttribute) { + this.sourceAttribute = sourceAttribute; + this.sourceType = sourceType; + this.inherit = false; + } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatisticConfig.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatisticConfig.java new file mode 100644 index 0000000000..bfe7d29bb6 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatisticConfig.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 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.queue.kafka; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class TbKafkaConsumerStatisticConfig { + @Value("${queue.kafka.consumer-stats.enabled:true}") + private Boolean enabled; + @Value("${queue.kafka.consumer-stats.print-interval-ms:60000}") + private Long printIntervalMs; + @Value("${queue.kafka.consumer-stats.kafka-response-timeout-ms:1000}") + private Long kafkaResponseTimeoutMs; +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatsService.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatsService.java new file mode 100644 index 0000000000..0ccf9ba42c --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatsService.java @@ -0,0 +1,184 @@ +/** + * Copyright © 2016-2021 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.queue.kafka; + +import lombok.Builder; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.consumer.OffsetAndMetadata; +import org.apache.kafka.common.TopicPartition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.queue.discovery.PartitionService; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +@RequiredArgsConstructor +@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") +public class TbKafkaConsumerStatsService { + private final Set monitoredGroups = ConcurrentHashMap.newKeySet(); + + private final TbKafkaSettings kafkaSettings; + private final TbKafkaConsumerStatisticConfig statsConfig; + private final PartitionService partitionService; + + private AdminClient adminClient; + private Consumer consumer; + private ScheduledExecutorService statsPrintScheduler; + + @PostConstruct + public void init() { + if (!statsConfig.getEnabled()) { + return; + } + this.adminClient = AdminClient.create(kafkaSettings.toAdminProps()); + this.statsPrintScheduler = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("kafka-consumer-stats")); + + Properties consumerProps = kafkaSettings.toConsumerProps(); + consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "consumer-stats-loader-client"); + consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer-stats-loader-client-group"); + this.consumer = new KafkaConsumer<>(consumerProps); + + startLogScheduling(); + } + + private void startLogScheduling() { + Duration timeoutDuration = Duration.ofMillis(statsConfig.getKafkaResponseTimeoutMs()); + statsPrintScheduler.scheduleWithFixedDelay(() -> { + if (!isStatsPrintRequired()) { + return; + } + for (String groupId : monitoredGroups) { + try { + Map groupOffsets = adminClient.listConsumerGroupOffsets(groupId).partitionsToOffsetAndMetadata() + .get(statsConfig.getKafkaResponseTimeoutMs(), TimeUnit.MILLISECONDS); + Map endOffsets = consumer.endOffsets(groupOffsets.keySet(), timeoutDuration); + + List lagTopicsStats = getTopicsStatsWithLag(groupOffsets, endOffsets); + if (!lagTopicsStats.isEmpty()) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < lagTopicsStats.size(); i++) { + builder.append(lagTopicsStats.get(i).toString()); + if (i != lagTopicsStats.size() - 1) { + builder.append(", "); + } + } + log.info("[{}] Topic partitions with lag: [{}].", groupId, builder.toString()); + } + } catch (Exception e) { + log.warn("[{}] Failed to get consumer group stats. Reason - {}.", groupId, e.getMessage()); + log.trace("Detailed error: ", e); + } + } + + }, statsConfig.getPrintIntervalMs(), statsConfig.getPrintIntervalMs(), TimeUnit.MILLISECONDS); + } + + private boolean isStatsPrintRequired() { + boolean isMyRuleEnginePartition = partitionService.resolve(ServiceType.TB_RULE_ENGINE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition(); + boolean isMyCorePartition = partitionService.resolve(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition(); + return log.isInfoEnabled() && (isMyRuleEnginePartition || isMyCorePartition); + } + + private List getTopicsStatsWithLag(Map groupOffsets, Map endOffsets) { + List consumerGroupStats = new ArrayList<>(); + for (TopicPartition topicPartition : groupOffsets.keySet()) { + long endOffset = endOffsets.get(topicPartition); + long committedOffset = groupOffsets.get(topicPartition).offset(); + long lag = endOffset - committedOffset; + if (lag != 0) { + GroupTopicStats groupTopicStats = GroupTopicStats.builder() + .topic(topicPartition.topic()) + .partition(topicPartition.partition()) + .committedOffset(committedOffset) + .endOffset(endOffset) + .lag(lag) + .build(); + consumerGroupStats.add(groupTopicStats); + } + } + return consumerGroupStats; + } + + public void registerClientGroup(String groupId) { + if (statsConfig.getEnabled() && !StringUtils.isEmpty(groupId)) { + monitoredGroups.add(groupId); + } + } + + public void unregisterClientGroup(String groupId) { + if (statsConfig.getEnabled() && !StringUtils.isEmpty(groupId)) { + monitoredGroups.remove(groupId); + } + } + + @PreDestroy + public void destroy() { + if (statsPrintScheduler != null) { + statsPrintScheduler.shutdownNow(); + } + if (adminClient != null) { + adminClient.close(); + } + if (consumer != null) { + consumer.close(); + } + } + + + @Builder + @Data + private static class GroupTopicStats { + private String topic; + private int partition; + private long committedOffset; + private long endOffset; + private long lag; + + @Override + public String toString() { + return "[" + + "topic=[" + topic + ']' + + ", partition=[" + partition + "]" + + ", committedOffset=[" + committedOffset + "]" + + ", endOffset=[" + endOffset + "]" + + ", lag=[" + lag + "]" + + "]"; + } + } +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java index 8743f4d6cc..b1b3d5ce05 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java @@ -42,10 +42,13 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue private final KafkaConsumer consumer; private final TbKafkaDecoder decoder; + private final TbKafkaConsumerStatsService statsService; + private final String groupId; + @Builder private TbKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder decoder, String clientId, String groupId, String topic, - TbQueueAdmin admin) { + TbQueueAdmin admin, TbKafkaConsumerStatsService statsService) { super(topic); Properties props = settings.toConsumerProps(); props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); @@ -53,6 +56,13 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); } + this.statsService = statsService; + this.groupId = groupId; + + if (statsService != null) { + statsService.registerClientGroup(groupId); + } + this.admin = admin; this.consumer = new KafkaConsumer<>(props); this.decoder = decoder; @@ -96,6 +106,8 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue consumer.unsubscribe(); consumer.close(); } + if (statsService != null) { + statsService.unregisterClientGroup(groupId); + } } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index eebffc37b0..e07f3e43d3 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -39,6 +39,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; +import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaProducerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaSettings; @@ -65,6 +66,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; + private final TbKafkaConsumerStatsService consumerStatsService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -79,6 +81,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, + TbKafkaConsumerStatsService consumerStatsService, TbKafkaTopicConfigs kafkaTopicConfigs) { this.partitionService = partitionService; this.kafkaSettings = kafkaSettings; @@ -88,6 +91,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.jsInvokeSettings = jsInvokeSettings; + this.consumerStatsService = consumerStatsService; this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); @@ -156,6 +160,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi consumerBuilder.groupId("re-" + queueName + "-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(ruleEngineAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -168,6 +173,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi consumerBuilder.groupId("monolith-rule-engine-notifications-consumer-" + serviceInfoProvider.getServiceId()); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(notificationAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -180,6 +186,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi consumerBuilder.groupId("monolith-core-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -192,6 +199,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi consumerBuilder.groupId("monolith-core-notifications-consumer-" + serviceInfoProvider.getServiceId()); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(notificationAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -204,6 +212,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi consumerBuilder.groupId("monolith-transport-api-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(transportApiAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -237,6 +246,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); } ); + responseBuilder.statsService(consumerStatsService); responseBuilder.admin(jsExecutorAdmin); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder @@ -259,6 +269,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi consumerBuilder.groupId("monolith-us-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index 15771a3b78..24e80adec5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -39,6 +39,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; +import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaProducerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaSettings; @@ -62,6 +63,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; + private final TbKafkaConsumerStatsService consumerStatsService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -75,6 +77,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbQueueRuleEngineSettings ruleEngineSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, + TbKafkaConsumerStatsService consumerStatsService, TbKafkaTopicConfigs kafkaTopicConfigs) { this.partitionService = partitionService; this.kafkaSettings = kafkaSettings; @@ -83,6 +86,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { this.ruleEngineSettings = ruleEngineSettings; this.transportApiSettings = transportApiSettings; this.jsInvokeSettings = jsInvokeSettings; + this.consumerStatsService = consumerStatsService; this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); @@ -150,6 +154,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { consumerBuilder.groupId("tb-core-node"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -162,6 +167,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { consumerBuilder.groupId("tb-core-notifications-node-" + serviceInfoProvider.getServiceId()); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(notificationAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -174,6 +180,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { consumerBuilder.groupId("tb-core-transport-api-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(transportApiAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -208,6 +215,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { } ); responseBuilder.admin(jsExecutorAdmin); + responseBuilder.statsService(consumerStatsService); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); @@ -229,6 +237,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { consumerBuilder.groupId("tb-core-us-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java index b45ec62b04..a8247dc11e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java @@ -37,6 +37,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; +import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaProducerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaSettings; @@ -59,6 +60,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { private final TbQueueCoreSettings coreSettings; private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; + private final TbKafkaConsumerStatsService consumerStatsService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -70,6 +72,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, + TbKafkaConsumerStatsService consumerStatsService, TbKafkaTopicConfigs kafkaTopicConfigs) { this.partitionService = partitionService; this.kafkaSettings = kafkaSettings; @@ -77,6 +80,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; this.jsInvokeSettings = jsInvokeSettings; + this.consumerStatsService = consumerStatsService; this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); @@ -145,6 +149,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { consumerBuilder.groupId("re-" + queueName + "-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(ruleEngineAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -157,6 +162,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { consumerBuilder.groupId("tb-rule-engine-notifications-node-" + serviceInfoProvider.getServiceId()); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(notificationAdmin); + consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -181,6 +187,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { } ); responseBuilder.admin(jsExecutorAdmin); + responseBuilder.statsService(consumerStatsService); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java index 0c64a57304..8c0c6999d5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java @@ -32,6 +32,7 @@ import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; +import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaProducerTemplate; import org.thingsboard.server.queue.kafka.TbKafkaSettings; @@ -54,6 +55,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; + private final TbKafkaConsumerStatsService consumerStatsService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -66,6 +68,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { TbQueueRuleEngineSettings ruleEngineSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, + TbKafkaConsumerStatsService consumerStatsService, TbKafkaTopicConfigs kafkaTopicConfigs) { this.kafkaSettings = kafkaSettings; this.serviceInfoProvider = serviceInfoProvider; @@ -73,6 +76,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { this.ruleEngineSettings = ruleEngineSettings; this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; + this.consumerStatsService = consumerStatsService; this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); @@ -95,6 +99,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { responseBuilder.groupId("transport-node-" + serviceInfoProvider.getServiceId()); responseBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); responseBuilder.admin(transportApiAdmin); + responseBuilder.statsService(consumerStatsService); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); @@ -136,6 +141,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { responseBuilder.groupId("transport-node-" + serviceInfoProvider.getServiceId()); responseBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); responseBuilder.admin(notificationAdmin); + responseBuilder.statsService(consumerStatsService); return responseBuilder.build(); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index ed7d1d9da1..68fdc94dac 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -25,20 +25,18 @@ import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuil import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; import java.math.BigInteger; import java.security.AlgorithmParameters; -import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.CertificateEncodingException; @@ -47,16 +45,17 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; @Slf4j @@ -65,6 +64,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle public class LwM2MTransportBootstrapServerConfiguration { private PublicKey publicKey; private PrivateKey privateKey; + private boolean pskMode = false; @Autowired private LwM2MTransportContextBootstrap contextBs; @@ -78,61 +78,53 @@ public class LwM2MTransportBootstrapServerConfiguration { @Autowired private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore; - @Primary - @Bean(name = "leshanBootstrapX509") - @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_x509:false}'=='true')") - public LeshanBootstrapServer getLeshanBootstrapServerX509() { - log.info("Prepare and start BootstrapServerX509... PostConstruct"); - return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecX509(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortX509(), X509); - } - - @Bean(name = "leshanBootstrapPsk") - @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_psk:false}'=='true')") - public LeshanBootstrapServer getLeshanBootstrapServerPsk() { - log.info("Prepare and start BootstrapServerRsk... PostConstruct"); - return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecPsk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortPsk(), PSK); - } - @Bean(name = "leshanBootstrapRpk") - @ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_rpk:false}'=='true')") - public LeshanBootstrapServer getLeshanBootstrapServerRpk() { - log.info("Prepare and start BootstrapServerRpk... PostConstruct"); - return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecRpk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortRpk(), RPK); + @Bean + public LeshanBootstrapServer getLeshanBootstrapServer() { + log.info("Prepare and start BootstrapServer... PostConstruct"); + return this.getLhBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(), this.contextBs.getCtxBootStrap().getBootstrapPortSecurity()); } - public LeshanBootstrapServer getLeshanBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort, LwM2MSecurityMode dtlsMode) { + public LeshanBootstrapServer getLhBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort) { LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder(); builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec); - builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapSecureHost(), bootstrapSecurePort); + builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapHostSecurity(), bootstrapSecurePort); /** Create CoAP Config */ - builder.setCoapConfig(getCoapConfig (bootstrapPortNoSec, bootstrapSecurePort)); + builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort)); - /** ConfigStore */ + /** Define model provider (Create Models )*/ + builder.setModel(new StaticModel(contextS.getCtxServer().getModelsValue())); + + /** Create credentials */ + this.setServerWithCredentials(builder); + + /** Set securityStore with new ConfigStore */ builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); /** SecurityStore */ builder.setSecurityStore(lwM2MBootstrapSecurityStore); - /** Define model provider (Create Models )*/ - builder.setModel(new StaticModel(contextS.getCtxServer().getModelsValue())); - - /** Create credentials */ - LwM2MSetSecurityStoreBootstrap(builder, dtlsMode); /** Create and Set DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); - if (dtlsMode==PSK) { - dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers()); - dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getCtxServer().isRecommendedSupportedGroups()); - dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256); + dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getCtxServer().isRecommendedSupportedGroups()); + dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers()); + if (this.pskMode) { + dtlsConfig.setSupportedCipherSuites( + TLS_PSK_WITH_AES_128_CCM_8, + TLS_PSK_WITH_AES_128_CBC_SHA256); } - else { - dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers()); -// dtlsConfig.setRecommendedSupportedGroupsOnly(false); + else { + dtlsConfig.setSupportedCipherSuites( + TLS_PSK_WITH_AES_128_CCM_8, + TLS_PSK_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); } - builder.setDtlsConfig(dtlsConfig); + /** Set DTLS Config */ + builder.setDtlsConfig(dtlsConfig); BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore); builder.setSessionManager(sessionManager); @@ -141,150 +133,157 @@ public class LwM2MTransportBootstrapServerConfiguration { return builder.build(); } - public void LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MSecurityMode dtlsMode) { - - /** Set securityStore with new registrationStore */ - - switch (dtlsMode) { - /** Use No_Sec only */ - case NO_SEC: - setServerWithX509Cert(builder, NO_SEC.code); - break; - /** Use PSK/RPK */ - case PSK: - break; - case RPK: - setRPK(builder); - break; - case X509: - setServerWithX509Cert(builder, X509.code); - break; - /** Use X509_EST only */ - case X509_EST: - // TODO support sentinel pool and make pool configurable - break; - /** Use ather X509, PSK, No_Sec ?? */ - default: - break; - } - } - - private void setRPK(LeshanBootstrapServerBuilder builder) { - try { - /** Get Elliptic Curve Parameter spec for secp256r1 */ - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) { - /** Get point values */ - byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray()); - byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray()); - /** Create key specs */ - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - /** Get keys */ - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - } - if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) { - /** Get point values */ - byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray()); - /** Create key specs */ - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - /** Get keys */ - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - } - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && - this.privateKey != null && this.privateKey.getEncoded().length > 0) { - builder.setPublicKey(this.publicKey); - builder.setPrivateKey(this.privateKey); - this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey); - getParamsRPK(); - } - } catch (GeneralSecurityException | IllegalArgumentException e) { - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage()); - throw new RuntimeException(e); - } - } - - private void setServerWithX509Cert(LeshanBootstrapServerBuilder builder, int securityModeCode) { + private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) { try { if (this.contextS.getCtxServer().getKeyStoreValue() != null) { KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue(); - setBuilderX509(builder); - X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias()); - if (rootCAX509Cert != null && securityModeCode == X509.code) { - X509Certificate[] trustedCertificates = new X509Certificate[1]; - trustedCertificates[0] = rootCAX509Cert; - builder.setTrustedCertificates(trustedCertificates); - } else { - /** by default trust all */ - builder.setTrustedCertificates(new X509Certificate[0]); + if (this.setBuilderX509(builder)) { + X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias()); + if (rootCAX509Cert != null) { + X509Certificate[] trustedCertificates = new X509Certificate[1]; + trustedCertificates[0] = rootCAX509Cert; + builder.setTrustedCertificates(trustedCertificates); + } else { + /** by default trust all */ + builder.setTrustedCertificates(new X509Certificate[0]); + } } - } - else { + } else if (this.setServerRPK(builder)) { + this.infoPramsUri("RPK"); + this.infoParamsBootstrapServerKey(this.publicKey, this.privateKey); + } else { /** by default trust all */ builder.setTrustedCertificates(new X509Certificate[0]); - log.error("Unable to load X509 files for BootStrapServer"); + log.info("Unable to load X509 files for BootStrapServer"); + this.pskMode = true; + this.infoPramsUri("PSK"); } } catch (KeyStoreException ex) { log.error("[{}] Unable to load X509 files server", ex.getMessage()); } - } - private void setBuilderX509(LeshanBootstrapServerBuilder builder) { + private boolean setBuilderX509(LeshanBootstrapServerBuilder builder) { /** * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks */ try { X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias()); - this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray()); - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { - builder.setPrivateKey(this.privateKey); - } - if (serverCertificate != null) { + PrivateKey privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray()); + PublicKey publicKey = serverCertificate.getPublicKey(); + if (serverCertificate != null && + privateKey != null && privateKey.getEncoded().length > 0 && + publicKey != null && publicKey.getEncoded().length > 0) { + builder.setPublicKey(serverCertificate.getPublicKey()); + builder.setPrivateKey(privateKey); builder.setCertificateChain(new X509Certificate[]{serverCertificate}); - this.infoParamsX509(serverCertificate); + this.infoParamsServerX509(serverCertificate, publicKey, privateKey); + return true; + } else { + return false; } } catch (Exception ex) { log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); + return false; } } - private void getParamsRPK() { - if (this.publicKey instanceof ECPublicKey) { - /** Get x coordinate */ - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray(); - if (x[0] == 0) - x = Arrays.copyOfRange(x, 1, x.length); - - /** Get Y coordinate */ - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray(); - if (y[0] == 0) - y = Arrays.copyOfRange(y, 1, y.length); - - /** Get Curves params */ - String params = ((ECPublicKey) this.publicKey).getParams().toString(); - log.info( - " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]", - params, Hex.encodeHexString(x), Hex.encodeHexString(y), - Hex.encodeHexString(this.publicKey.getEncoded()), - Hex.encodeHexString(this.privateKey.getEncoded())); - } else { - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported)."); + private void infoParamsServerX509(X509Certificate certificate, PublicKey publicKey, PrivateKey privateKey) { + try { + this.infoPramsUri("X509"); + log.info("\n- X509 Certificate (Hex): [{}]", + Hex.encodeHexString(certificate.getEncoded())); + this.infoParamsBootstrapServerKey(publicKey, privateKey); + } catch (CertificateEncodingException e) { + log.error("", e); } } - private void infoParamsX509(X509Certificate certificate) { + private void infoPramsUri(String mode) { + log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]", + mode, + this.contextBs.getCtxBootStrap().getBootstrapHost() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(), + this.contextBs.getCtxBootStrap().getBootstrapHostSecurity() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortSecurity()); + } + + + private boolean setServerRPK(LeshanBootstrapServerBuilder builder) { try { - log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]", - Hex.encodeHexString(certificate.getEncoded()), - Hex.encodeHexString(this.privateKey.getEncoded())); - } catch (CertificateEncodingException e) { - log.error("", e); + this.generateKeyForBootstrapRPK(); + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && + this.privateKey != null && this.privateKey.getEncoded().length > 0) { + builder.setPublicKey(this.publicKey); +// builder.setCertificateChain(new X509Certificate[] { serverCertificate }); + /// Trust all certificates. + builder.setTrustedCertificates(new X509Certificate[0]); + builder.setPrivateKey(this.privateKey); + return true; + } + } catch (NoSuchAlgorithmException | InvalidParameterSpecException | InvalidKeySpecException e) { + log.error("Fail create Bootstrap Server with RPK", e); } + return false; } + /** + * From yml: bootstrap + * public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}" + * public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}" + * private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}" + */ + private void generateKeyForBootstrapRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException { + /** Get Elliptic Curve Parameter spec for secp256r1 */ + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) { + /** Get point values */ + byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray()); + byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray()); + /** Create key specs */ + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + /** Get public key */ + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + } + if (this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded().isEmpty()) { + /** Get private key */ + byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded().toCharArray()); + try { + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS)); + } catch (InvalidKeySpecException ignore2) { + log.error("Invalid Bootstrap Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded()); + } + } + } + + private void infoParamsBootstrapServerKey(PublicKey publicKey, PrivateKey privateKey) { + /** Get x coordinate */ + byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray(); + if (x[0] == 0) + x = Arrays.copyOfRange(x, 1, x.length); + + /** Get Y coordinate */ + byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray(); + if (y[0] == 0) + y = Arrays.copyOfRange(y, 1, y.length); + + /** Get Curves params */ + String params = ((ECPublicKey) publicKey).getParams().toString(); + String privHex = Hex.encodeHexString(privateKey.getEncoded()); + log.info("\n- Public Key (Hex): [{}] \n" + + "- Private Key (Hex): [{}], \n" + + "public_x: \"${LWM2M_SERVER_PUBLIC_X_BS:{}}\" \n" + + "public_y: \"${LWM2M_SERVER_PUBLIC_Y_BS:{}}\" \n" + + "private_encoded: \"${LWM2M_SERVER_PRIVATE_ENCODED_BS:{}}\" \n" + + "- Elliptic Curve parameters : [{}]", + Hex.encodeHexString(publicKey.getEncoded()), + privHex, + Hex.encodeHexString(x), + Hex.encodeHexString(y), + privHex, + params); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java index 6ca751b138..c3d6d9d0c1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.lwm2m.bootstrap; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; @@ -31,39 +30,20 @@ import javax.annotation.PreDestroy; public class LwM2MTransportBootstrapServerInitializer { @Autowired(required = false) - @Qualifier("leshanBootstrapX509") - private LeshanBootstrapServer lhBServerCert; - - @Autowired(required = false) - @Qualifier("leshanBootstrapPsk") - private LeshanBootstrapServer lhBServerPsk; - - @Autowired(required = false) - @Qualifier("leshanBootstrapRpk") - private LeshanBootstrapServer lhBServerRpk; + private LeshanBootstrapServer lhBServer; @Autowired private LwM2MTransportContextBootstrap contextBS; @PostConstruct public void init() { - if (this.contextBS.getCtxBootStrap().getBootstrapStartPsk()) { - this.lhBServerPsk.start(); - } - if (this.contextBS.getCtxBootStrap().getBootstrapStartRpk()) { - this.lhBServerRpk.start(); - } - if (this.contextBS.getCtxBootStrap().getBootstrapStartX509()) { - this.lhBServerCert.start(); - } + this.lhBServer.start(); } @PreDestroy public void shutdown() throws InterruptedException { log.info("Stopping LwM2M transport Bootstrap Server!"); - lhBServerPsk.destroy(); - lhBServerRpk.destroy(); - lhBServerCert.destroy(); + lhBServer.destroy(); log.info("LwM2M transport Bootstrap Server stopped!"); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java index f4abbd2fda..4df520543b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java @@ -103,18 +103,18 @@ public class LWM2MGenerationPSkRPkECC { /** Get Curves params */ String privHex = Hex.encodeHexString(privKey.getEncoded()); log.info("\nCreating new RPK for the next start... \n" + - " Elliptic Curve parameters : [{}] \n" + + " Public Key (Hex): [{}]\n" + + " Private Key (Hex): [{}]" + " public_x : [{}] \n" + " public_y : [{}] \n" + - " private_s : [{}] \n" + - " Public Key (Hex): [{}]\n" + - " Private Key (Hex): [{}]", - ecPublicKey.getParams().toString(), + " private_encode : [{}] \n" + + " Elliptic Curve parameters : [{}] \n", + Hex.encodeHexString(pubKey.getEncoded()), + privHex, Hex.encodeHexString(x), Hex.encodeHexString(y), - privHex.substring(privHex.length() - 64), - Hex.encodeHexString(pubKey.getEncoded()), - Hex.encodeHexString(privKey.getEncoded())); + privHex, + ecPublicKey.getParams().toString()); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java index 388138d8c3..5981a05e06 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java @@ -19,37 +19,25 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; -import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder; import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.model.VersionedModelProvider; -import org.eclipse.leshan.server.redis.RedisRegistrationStore; -import org.eclipse.leshan.server.redis.RedisSecurityStore; import org.eclipse.leshan.server.security.DefaultAuthorizer; -import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.SecurityChecker; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; +import org.springframework.stereotype.Component; import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.util.Pool; import java.math.BigInteger; -import java.net.URI; -import java.net.URISyntaxException; import java.security.AlgorithmParameters; -import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.CertificateEncodingException; @@ -58,26 +46,26 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; +import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; - @Slf4j -@ComponentScan("org.thingsboard.server.transport.lwm2m.server") -@ComponentScan("org.thingsboard.server.transport.lwm2m.utils") -@Configuration("LwM2MTransportServerConfiguration") +@Component("LwM2MTransportServerConfiguration") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportServerConfiguration { private PublicKey publicKey; private PrivateKey privateKey; + private boolean pskMode = false; @Autowired private LwM2MTransportContextServer context; @@ -85,35 +73,18 @@ public class LwM2MTransportServerConfiguration { @Autowired private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; - @Primary - @Bean(name = "leshanServerPsk") - @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_psk:false}'=='true')") - public LeshanServer getLeshanServerPsk() { - log.info("Starting LwM2M transport ServerPsk... PostConstruct"); - return getLeshanServer(this.context.getCtxServer().getServerPortNoSecPsk(), this.context.getCtxServer().getServerPortPsk(), PSK); - } - - @Bean(name = "leshanServerRpk") - @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_rpk:false}'=='true')") - public LeshanServer getLeshanServerRpk() { - log.info("Starting LwM2M transport ServerRpk... PostConstruct"); - return getLeshanServer(this.context.getCtxServer().getServerPortNoSecRpk(), this.context.getCtxServer().getServerPortRpk(), RPK); - } - - @Bean(name = "leshanServerX509") - @ConditionalOnExpression("('${transport.lwm2m.server.secure.start_x509:false}'=='true')") - public LeshanServer getLeshanServerX509() { - log.info("Starting LwM2M transport ServerX509... PostConstruct"); - return getLeshanServer(this.context.getCtxServer().getServerPortNoSecX509(), this.context.getCtxServer().getServerPortX509(), X509); + @Bean + public LeshanServer getLeshanServer() { + log.info("Starting LwM2M transport Server... PostConstruct"); + return this.getLhServer(this.context.getCtxServer().getServerPortNoSec(), this.context.getCtxServer().getServerPortSecurity()); } - private LeshanServer getLeshanServer(Integer serverPortNoSec, Integer serverSecurePort, LwM2MSecurityMode dtlsMode) { + private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) { LeshanServerBuilder builder = new LeshanServerBuilder(); builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPortNoSec); - builder.setLocalSecureAddress(this.context.getCtxServer().getServerSecureHost(), serverSecurePort); - builder.setEncoder(new DefaultLwM2mNodeEncoder()); - LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder(); - builder.setDecoder(decoder); + builder.setLocalSecureAddress(this.context.getCtxServer().getServerHostSecurity(), serverSecurePort); + builder.setDecoder(new DefaultLwM2mNodeDecoder()); + /** Use a magic converter to support bad type send by the UI. */ builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); /** Create CoAP Config */ @@ -123,181 +94,76 @@ public class LwM2MTransportServerConfiguration { LwM2mModelProvider modelProvider = new VersionedModelProvider(this.context.getCtxServer().getModelsValue()); builder.setObjectModelProvider(modelProvider); - /** Create DTLS security mode - * There can be only one DTLS security mode - */ - this.LwM2MSetSecurityStoreServer(builder, dtlsMode); + /** Create credentials */ + this.setServerWithCredentials(builder); + + /** Set securityStore with new registrationStore */ + builder.setSecurityStore(lwM2mInMemorySecurityStore); + /** Create DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); - if (dtlsMode==PSK) { - dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isRecommendedCiphers()); - dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getCtxServer().isRecommendedSupportedGroups()); - dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256); + dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getCtxServer().isRecommendedSupportedGroups()); + dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isRecommendedCiphers()); + if (this.pskMode) { + dtlsConfig.setSupportedCipherSuites( + TLS_PSK_WITH_AES_128_CCM_8, + TLS_PSK_WITH_AES_128_CBC_SHA256); } - else { - dtlsConfig.setRecommendedSupportedGroupsOnly(!this.context.getCtxServer().isRecommendedSupportedGroups()); - dtlsConfig.setRecommendedCipherSuitesOnly(!this.context.getCtxServer().isRecommendedCiphers()); + else { + dtlsConfig.setSupportedCipherSuites( + TLS_PSK_WITH_AES_128_CCM_8, + TLS_PSK_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); } - /** Set DTLS Config */ - builder.setDtlsConfig(dtlsConfig); - /** Use a magic converter to support bad type send by the UI. */ - builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); + /** Set DTLS Config */ + builder.setDtlsConfig(dtlsConfig); /** Create LWM2M server */ return builder.build(); } - private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) { - /** Set securityStore with new registrationStore */ - EditableSecurityStore securityStore = lwM2mInMemorySecurityStore; - switch (dtlsMode) { - /** Use PSK only */ - case PSK: - generatePSK_RPK(); - infoParamsPSK(); - break; - /** Use RPK only */ - case RPK: - generatePSK_RPK(); - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && - this.privateKey != null && this.privateKey.getEncoded().length > 0) { - builder.setPublicKey(this.publicKey); - builder.setPrivateKey(this.privateKey); - infoParamsRPK(); - } - break; - /** Use x509 only */ - case X509: - setServerWithX509Cert(builder); - break; - /** No security */ - case NO_SEC: - builder.setTrustedCertificates(new X509Certificate[0]); - break; - /** Use x509 with EST */ - case X509_EST: - // TODO support sentinel pool and make pool configurable - break; - case REDIS: - /** - * Set securityStore with new registrationStore (if use redis store) - * Connect to redis - */ - Pool jedis = null; - try { - jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl())); - securityStore = new RedisSecurityStore(jedis); - builder.setRegistrationStore(new RedisRegistrationStore(jedis)); - } catch (URISyntaxException e) { - log.error("", e); - } - break; - default: - } - - /** Set securityStore with registrationStore (if x509)*/ - if (dtlsMode == X509) { - builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() { - @Override - protected boolean matchX509Identity(String endpoint, String receivedX509CommonName, - String expectedX509CommonName) { - return endpoint.startsWith(expectedX509CommonName); - } - })); - } - - /** Set securityStore with new registrationStore */ - builder.setSecurityStore(securityStore); - } - - private void generatePSK_RPK() { - try { - /** Get Elliptic Curve Parameter spec for secp256r1 */ - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); - algoParameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) { - /** Get point values */ - byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray()); - byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray()); - /** Create key specs */ - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), - parameterSpec); - /** Get keys */ - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); - } - if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) { - /** Get point values */ - byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray()); - /** Create key specs */ - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); - /** Get keys */ - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); - } - } catch (GeneralSecurityException | IllegalArgumentException e) { - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage()); - throw new RuntimeException(e); - } - } - - private void infoParamsPSK() { - log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]", - Hex.encodeHexString(this.privateKey.getEncoded()), - this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerPortPsk())); - } - - private void infoParamsRPK() { - if (this.publicKey instanceof ECPublicKey) { - /** Get x coordinate */ - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray(); - if (x[0] == 0) - x = Arrays.copyOfRange(x, 1, x.length); - - /** Get Y coordinate */ - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray(); - if (y[0] == 0) - y = Arrays.copyOfRange(y, 1, y.length); - - /** Get Curves params */ - String params = ((ECPublicKey) this.publicKey).getParams().toString(); - log.info( - " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]", - params, Hex.encodeHexString(x), Hex.encodeHexString(y), - Hex.encodeHexString(this.publicKey.getEncoded()), - Hex.encodeHexString(this.privateKey.getEncoded())); - } else { - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported)."); - } - } - - - private void setServerWithX509Cert(LeshanServerBuilder builder) { + private void setServerWithCredentials(LeshanServerBuilder builder) { try { if (this.context.getCtxServer().getKeyStoreValue() != null) { - setBuilderX509(builder); - X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias()); - if (rootCAX509Cert != null) { - X509Certificate[] trustedCertificates = new X509Certificate[1]; - trustedCertificates[0] = rootCAX509Cert; - builder.setTrustedCertificates(trustedCertificates); - } else { - /** by default trust all */ - builder.setTrustedCertificates(new X509Certificate[0]); + if (this.setBuilderX509(builder)) { + X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias()); + if (rootCAX509Cert != null) { + X509Certificate[] trustedCertificates = new X509Certificate[1]; + trustedCertificates[0] = rootCAX509Cert; + builder.setTrustedCertificates(trustedCertificates); + } else { + /** by default trust all */ + builder.setTrustedCertificates(new X509Certificate[0]); + } + /** Set securityStore with registrationStore*/ + builder.setAuthorizer(new DefaultAuthorizer(lwM2mInMemorySecurityStore, new SecurityChecker() { + @Override + protected boolean matchX509Identity(String endpoint, String receivedX509CommonName, + String expectedX509CommonName) { + return endpoint.startsWith(expectedX509CommonName); + } + })); } + } else if (this.setServerRPK(builder)) { + this.infoPramsUri("RPK"); + this.infoParamsServerKey(this.publicKey, this.privateKey); } else { /** by default trust all */ builder.setTrustedCertificates(new X509Certificate[0]); - log.error("Unable to load X509 files for LWM2MServer"); + log.info("Unable to load X509 files for LWM2MServer"); + this.pskMode = true; + this.infoPramsUri("PSK"); } } catch (KeyStoreException ex) { log.error("[{}] Unable to load X509 files server", ex.getMessage()); } } - private void setBuilderX509(LeshanServerBuilder builder) { + private boolean setBuilderX509(LeshanServerBuilder builder) { /** * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks @@ -305,38 +171,120 @@ public class LwM2MTransportServerConfiguration { try { X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias()); PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray()); - builder.setPrivateKey(privateKey); - builder.setCertificateChain(new X509Certificate[]{serverCertificate}); - this.infoParamsX509(serverCertificate, privateKey); + PublicKey publicKey = serverCertificate.getPublicKey(); + if (serverCertificate != null && + privateKey != null && privateKey.getEncoded().length > 0 && + publicKey != null && publicKey.getEncoded().length > 0) { + builder.setPublicKey(serverCertificate.getPublicKey()); + builder.setPrivateKey(privateKey); + builder.setCertificateChain(new X509Certificate[]{serverCertificate}); + this.infoParamsServerX509(serverCertificate, publicKey, privateKey); + return true; + } else { + return false; + } } catch (Exception ex) { log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); + return false; } -// /** -// * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE -// * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks -// */ -// try { -// X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerPrivateS()); -// this.privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray()); -// if (this.privateKey != null && this.privateKey.getEncoded().length > 0) { -// builder.setPrivateKey(this.privateKey); -// } -// if (serverCertificate != null) { -// builder.setCertificateChain(new X509Certificate[]{serverCertificate}); -// this.infoParamsX509(serverCertificate); -// } -// } catch (Exception ex) { -// log.error("[{}] Unable to load KeyStore files server", ex.getMessage()); -// } } - private void infoParamsX509(X509Certificate certificate, PrivateKey privateKey) { + private void infoParamsServerX509(X509Certificate certificate, PublicKey publicKey, PrivateKey privateKey) { try { - log.info("Server uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]", - Hex.encodeHexString(certificate.getEncoded()), - Hex.encodeHexString(privateKey.getEncoded())); + infoPramsUri("X509"); + log.info("\n- X509 Certificate (Hex): [{}]", + Hex.encodeHexString(certificate.getEncoded())); + this.infoParamsServerKey(publicKey, privateKey); } catch (CertificateEncodingException e) { log.error("", e); } } + + private void infoPramsUri(String mode) { + log.info("Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]", + mode, + this.context.getCtxServer().getServerHost() + ":" + this.context.getCtxServer().getServerPortNoSec(), + this.context.getCtxServer().getServerHostSecurity() + ":" + this.context.getCtxServer().getServerPortSecurity()); + } + + private boolean setServerRPK(LeshanServerBuilder builder) { + try { + this.generateKeyForRPK(); + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 && + this.privateKey != null && this.privateKey.getEncoded().length > 0) { + builder.setPublicKey(this.publicKey); + builder.setPrivateKey(this.privateKey); + return true; + } + } catch (NoSuchAlgorithmException | InvalidParameterSpecException | InvalidKeySpecException e) { + log.error("Fail create Server with RPK", e); + } + return false; + } + + + /** + * From yml: server + * public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}" + * public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}" + * private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}" + */ + private void generateKeyForRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException { + /** Get Elliptic Curve Parameter spec for secp256r1 */ + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); + algoParameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); + if (this.context.getCtxServer().getServerPublicX() != null && + !this.context.getCtxServer().getServerPublicX().isEmpty() && + this.context.getCtxServer().getServerPublicY() != null && + !this.context.getCtxServer().getServerPublicY().isEmpty()) { + /** Get point values */ + byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray()); + byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray()); + /** Create key specs */ + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), + parameterSpec); + /** Get keys */ + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); + } + if (this.context.getCtxServer().getServerPrivateEncoded() != null && + !this.context.getCtxServer().getServerPrivateEncoded().isEmpty()) { + /** Get private key */ + byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateEncoded().toCharArray()); + try { + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS)); + } catch (InvalidKeySpecException ignore2) { + log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", this.context.getCtxServer().getServerPrivateEncoded()); + } + } + } + + private void infoParamsServerKey(PublicKey publicKey, PrivateKey privateKey) { + /** Get x coordinate */ + byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray(); + if (x[0] == 0) + x = Arrays.copyOfRange(x, 1, x.length); + + /** Get Y coordinate */ + byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray(); + if (y[0] == 0) + y = Arrays.copyOfRange(y, 1, y.length); + + /** Get Curves params */ + String params = ((ECPublicKey) publicKey).getParams().toString(); + String privHex = Hex.encodeHexString(privateKey.getEncoded()); + log.info(" \n- Public Key (Hex): [{}] \n" + + "- Private Key (Hex): [{}], \n" + + "public_x: \"${LWM2M_SERVER_PUBLIC_X:{}}\" \n" + + "public_y: \"${LWM2M_SERVER_PUBLIC_Y:{}}\" \n" + + "private_encoded: \"${LWM2M_SERVER_PRIVATE_ENCODED:{}}\" \n" + + "- Elliptic Curve parameters : [{}]", + Hex.encodeHexString(publicKey.getEncoded()), + privHex, + Hex.encodeHexString(x), + Hex.encodeHexString(y), + privHex, + params); + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java index 90215326b4..2805fd7f91 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.lwm2m.server; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.server.californium.LeshanServer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; @@ -31,69 +30,35 @@ import javax.annotation.PreDestroy; @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") public class LwM2MTransportServerInitializer { - @Autowired private LwM2MTransportServiceImpl service; @Autowired(required = false) - @Qualifier("leshanServerX509") - private LeshanServer lhServerX509; - - @Autowired(required = false) - @Qualifier("leshanServerPsk") - private LeshanServer lhServerPsk; - - @Autowired(required = false) - @Qualifier("leshanServerRpk") - private LeshanServer lhServerRpk; + private LeshanServer leshanServer; @Autowired private LwM2MTransportContextServer context; @PostConstruct public void init() { - if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC(); - if (this.context.getCtxServer().isServerStartPsk()) { - this.startLhServerPsk(); - } - if (this.context.getCtxServer().isServerStartRpk()) { - this.startLhServerRpk(); + if (this.context.getCtxServer().getEnableGenNewKeyPskRpk()) { + new LWM2MGenerationPSkRPkECC(); } - if (this.context.getCtxServer().isServerStartX509()) { - this.startLhServerX509(); - } - } - - private void startLhServerPsk() { - this.lhServerPsk.start(); - LwM2mServerListener lhServerPskListener = new LwM2mServerListener(this.lhServerPsk, service); - this.lhServerPsk.getRegistrationService().addListener(lhServerPskListener.registrationListener); - this.lhServerPsk.getPresenceService().addListener(lhServerPskListener.presenceListener); - this.lhServerPsk.getObservationService().addListener(lhServerPskListener.observationListener); - } - - private void startLhServerRpk() { - this.lhServerRpk.start(); - LwM2mServerListener lhServerRpkListener = new LwM2mServerListener(this.lhServerRpk, service); - this.lhServerRpk.getRegistrationService().addListener(lhServerRpkListener.registrationListener); - this.lhServerRpk.getPresenceService().addListener(lhServerRpkListener.presenceListener); - this.lhServerRpk.getObservationService().addListener(lhServerRpkListener.observationListener); + this.startLhServer(); } - private void startLhServerX509() { - this.lhServerX509.start(); - LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.lhServerX509, service); - this.lhServerX509.getRegistrationService().addListener(lhServerCertListener.registrationListener); - this.lhServerX509.getPresenceService().addListener(lhServerCertListener.presenceListener); - this.lhServerX509.getObservationService().addListener(lhServerCertListener.observationListener); + private void startLhServer() { + this.leshanServer.start(); + LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.leshanServer, service); + this.leshanServer.getRegistrationService().addListener(lhServerCertListener.registrationListener); + this.leshanServer.getPresenceService().addListener(lhServerCertListener.presenceListener); + this.leshanServer.getObservationService().addListener(lhServerCertListener.observationListener); } @PreDestroy public void shutdown() { log.info("Stopping LwM2M transport Server!"); - lhServerPsk.destroy(); - lhServerRpk.destroy(); - lhServerX509.destroy(); + leshanServer.destroy(); log.info("LwM2M transport Server stopped!"); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java index a3f86cba96..06a37a3290 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java @@ -74,7 +74,7 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { public SecurityInfo getByEndpoint(String endPoint) { readLock.lock(); try { - String registrationId = this.getByRegistrationId(endPoint, null); + String registrationId = this.getRegistrationId(endPoint, null); return (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getSecurityInfo() : this.addLwM2MClientToSession(endPoint); } finally { @@ -91,7 +91,7 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { public SecurityInfo getByIdentity(String identity) { readLock.lock(); try { - String integrationId = this.getByRegistrationId(null, identity); + String integrationId = this.getRegistrationId(null, identity); return (integrationId != null) ? sessions.get(integrationId).getSecurityInfo() : this.addLwM2MClientToSession(identity); } finally { readLock.unlock(); @@ -177,10 +177,10 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { } } - private String getByRegistrationId(String endPoint, String identity) { + private String getRegistrationId(String endPoint, String identity) { List registrationIds = (endPoint != null) ? - this.sessions.entrySet().stream().filter(model -> endPoint.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()) : - this.sessions.entrySet().stream().filter(model -> identity.equals(model.getValue().getIdentity())).map(model -> model.getKey()).collect(Collectors.toList()); + this.sessions.entrySet().stream().filter(model -> endPoint.equals(model.getValue().getEndPoint())).map(Map.Entry::getKey).collect(Collectors.toList()) : + this.sessions.entrySet().stream().filter(model -> identity.equals(model.getValue().getIdentity())).map(Map.Entry::getKey).collect(Collectors.toList()); return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null; } diff --git a/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks b/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks index 5fcb65d351..9f6748f8fd 100644 Binary files a/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks and b/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks differ diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh index 3167cca2b5..dcf38b4f10 100755 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh +++ b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Copyright © 2016-2021 The Thingsboard Authors # @@ -15,77 +15,35 @@ # limitations under the License. # -#p) CLIENT_CN=LwX50900000000 +#/home/nick/Igor_project/Thingsboard_Perfrmance_test/performance-tests/src/main/resources/credentials/shell/lwM2M_credentials.sh -p LwX509 -s 0 -f 2000 -a client_alias_ -e client_self_signed_ -b bootstrap -d server -j serverKeyStore.jks -k clientKeyStore.jks -c client_ks_password -w server_ks_password + +#p) CLIENT_CN=$CLIENT_PREFIX00000000 #s) client_start=0 #f) client_finish=1 -#a) CLIENT_ALIAS=client_alias_00000000 +#a) CLIENT_ALIAS=CLIENT_ALIAS_PREFIX_00000000 +#e) CLIENT_SELF_ALIAS=CLIENT_SELF_ALIAS_PREFIX_00000000 #b) BOOTSTRAP_ALIAS=bootstrap #d) SERVER_ALIAS=server #j) SERVER_STORE=serverKeyStore.jks #k) CLIENT_STORE=clientKeyStore.jks #c) CLIENT_STORE_PWD=client_ks_password #w) SERVER_STORE_PWD=server_ks_password +#l) ROOT_KEY_ALIAS=root_key_alias -#while test $# -gt 0; do -# case "$1" in -# -h|--help) -# echo "$package - attempt to capture frames" -# echo " " -# echo "$package [options] application [arguments]" -# echo " " -# echo "options:" -# echo "-h, --help show brief help" -# echo "-a, --action=ACTION specify an action to use" -# echo "-o, --output-dir=DIR specify a directory to store output in" -# exit 0 -# ;; -# -a) -# shift -# if test $# -gt 0; then -# export PROCESS=$1 -# else -# echo "no process specified" -# exit 1 -# fi -# shift -# ;; -# --action*) -# export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'` -# shift -# ;; -# -o) -# shift -# if test $# -gt 0; then -# export OUTPUT=$1 -# else -# echo "no output dir specified" -# exit 1 -# fi -# shift -# ;; -# --output-dir*) -# export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'` -# shift -# ;; -# *) -# break -# ;; -# esac -#done - - -while getopts p:s:f:a:b:d:j:k:c:w: flag; do +while getopts p:s:f:a:e:b:d:j:k:c:w:l: flag; do case "${flag}" in - p) client_prefix=${OPTARG} ;; + p) client_pref=${OPTARG} ;; s) client_start=${OPTARG} ;; f) client_finish=${OPTARG} ;; - a) client_alias=${OPTARG} ;; + a) client_alias_pref=${OPTARG} ;; + e) client_self_alias_pref=${OPTARG} ;; b) bootstrap_alias=${OPTARG} ;; d) server_alias=${OPTARG} ;; j) key_store_server_file=${OPTARG} ;; k) key_store_client_file=${OPTARG} ;; c) client_key_store_pwd=${OPTARG} ;; w) server_key_store_pwd=${OPTARG} ;; + w) root_key_alias=${OPTARG} ;; esac done @@ -96,9 +54,8 @@ cd $script_dir # source the properties: . ./lwM2M_keygen.properties - -if [ -n "$client_prefix" ]; then - CLIENT_PREFIX=$client_prefix +if [ -n "$client_pref" ]; then + CLIENT_PREFIX=$client_pref fi if [ -z "$client_start" ]; then @@ -109,8 +66,12 @@ if [ -z "$client_finish" ]; then client_finish=1 fi -if [ -n "$client_alias" ]; then - CLIENT_ALIAS=$client_alias +if [ -n "$client_alias_pref" ]; then + CLIENT_ALIAS_PREFIX=$client_alias_pref +fi + +if [ -n "$client_self_alias_pref" ]; then + CLIENT_SELF_ALIAS_PREFIX=$client_self_alias_pref fi if [ -n "$bootstrap_alias" ]; then @@ -137,23 +98,37 @@ if [ -n "$server_key_store_pwd" ]; then SERVER_STORE_PWD=$server_key_store_pwd fi +if [ -n "$root_key_alias" ]; then + ROOT_KEY_ALIAS=$root_key_alias +fi + +CLIENT_NUMBER=$client_start + echo "==Start==" echo "CLIENT_PREFIX: $CLIENT_PREFIX" echo "client_start: $client_start" echo "client_finish: $client_finish" -echo "CLIENT_ALIAS: $CLIENT_ALIAS" +echo "CLIENT_ALIAS_PREFIX: $CLIENT_ALIAS_PREFIX" +echo "CLIENT_SELF_ALIAS_PREFIX: $CLIENT_SELF_ALIAS_PREFIX" echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS" echo "SERVER_ALIAS: $SERVER_ALIAS" echo "SERVER_STORE: $SERVER_STORE" echo "CLIENT_STORE: $CLIENT_STORE" echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD" echo "SERVER_STORE_PWD: $SERVER_STORE_PWD" +echo "CLIENT_NUMBER: $CLIENT_NUMBER" +echo "ROOT_KEY_ALIAS: $ROOT_KEY_ALIAS" end_point() { echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)" } + client_alias_point() { - echo "$CLIENT_ALIAS$(printf "%08d" $CLIENT_NUMBER)" + echo "$CLIENT_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" +} + +client_self_alias_point() { + echo "$CLIENT_SELF_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" } # Generation of the keystore. @@ -264,73 +239,30 @@ keytool \ -keystore $SERVER_STORE \ -storepass $SERVER_STORE_PWD -echo -echo "${H1}Client Keystore : ${RESET}" -echo "${H1}==================${RESET}" -#echo "${H2}Creating client key and self-signed certificate with expected CN...${RESET}" -#keytool \ -# -genkeypair \ -# -alias $CLIENT_ALIAS \ -# -keyalg EC \ -# -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -# -validity $VALIDITY \ -# -storetype $STORETYPE \ -# -keypass $CLIENT_STORE_PWD \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD -#keytool \ -# -exportcert \ -# -alias $CLIENT_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD | \ -# keytool \ -# -importcert \ -# -alias $CLIENT_SELF_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD \ -# -noprompt - -echo -echo "${H2}Import root certificate just to be able to import need by root CA with expected CN...${RESET}" -keytool \ - -exportcert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | +if [ "$client_start" -lt "$client_finish" ]; then + echo + echo "${H2}Import root certificate just to be able to import need by root CA with expected CN to $CLIENT_STORE${RESET}" keytool \ - -importcert \ + -exportcert \ -alias $ROOT_KEY_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt - -#echo -#echo "${H2}Creating client certificate signed by root CA with expected CN...${RESET}" -#keytool \ -# -certreq \ -# -alias $CLIENT_ALIAS \ -# -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD | \ -# keytool \ -# -gencert \ -# -alias $ROOT_KEY_ALIAS \ -# -keystore $SERVER_STORE \ -# -storepass $SERVER_STORE_PWD \ -# -storetype $STORETYPE \ -# -validity $VALIDITY | \ -# keytool \ -# -importcert \ -# -alias $CLIENT_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD \ -# -noprompt + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD | + keytool \ + -importcert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD \ + -noprompt +fi cert_end_point() { - echo "${H2}Creating client key and self-signed certificate with expected CN $CLIENT_SELF_CN ${RESET}" + echo + echo "${H1}Client Keystore : ${RESET}" + echo "${H1}==================${RESET}" + echo "${H2}Creating client key and self-signed certificate with expected CN CLIENT_ALIAS: $CLIENT_ALIAS${RESET}" keytool \ -genkeypair \ - -alias $CLIENT_CN_ALIAS \ + -alias $CLIENT_ALIAS \ -keyalg EC \ -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -validity $VALIDITY \ @@ -340,7 +272,7 @@ cert_end_point() { -storepass $CLIENT_STORE_PWD keytool \ -exportcert \ - -alias $CLIENT_CN_ALIAS \ + -alias $CLIENT_ALIAS \ -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD | keytool \ @@ -349,13 +281,28 @@ cert_end_point() { -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD \ -noprompt +# +# echo +# echo "${H2}Import root certificate just to be able to import ned by root CA with expected CN...${RESET}" +# keytool \ +# -exportcert \ +# -alias $ROOT_KEY_ALIAS \ +# -keystore $SERVER_STORE \ +# -storepass $SERVER_STORE_PWD | +# keytool \ +# -importcert \ +# -alias $ROOT_KEY_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD \ +# -noprompt +# echo - echo "${H2}Creating client certificate signed by root CA with expected $CLIENT_CN_NAME ${RESET}" + echo "${H2}Creating client certificate signed by root CA with expected CN CLIENT_ALIAS: $CLIENT_ALIAS CLIENT_CN: $CLIENT_CN${RESET}" keytool \ -certreq \ - -alias $CLIENT_CN_ALIAS \ - -dname "CN=$CLIENT_CN_NAME, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -alias $CLIENT_ALIAS \ + -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD | keytool \ @@ -367,22 +314,30 @@ cert_end_point() { -validity $VALIDITY | keytool \ -importcert \ - -alias $CLIENT_CN_ALIAS \ + -alias $CLIENT_ALIAS \ -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD \ -noprompt } -while [ "$CLIENT_NUMBER" != "$client_finish" ]; do - CLIENT_CN_NAME=$(end_point) - CLIENT_CN_ALIAS=$(client_alias_point) - echo "$CLIENT_CN_NAME" - echo "$CLIENT_CN_ALIAS" - cert_end_point - CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) - echo "number $CLIENT_NUMBER" - echo "finish $client_finish" -done +if [ "$client_start" -lt "$client_finish" ]; then + echo "Файл содержит, как минимум, одно слово Bash." + echo + echo "==Start Client==" + while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do + echo "number $CLIENT_NUMBER" + echo "finish $client_finish" + CLIENT_CN=$(end_point) + CLIENT_ALIAS=$(client_alias_point) + CLIENT_SELF_ALIAS=$(client_self_alias_point) + echo "CLIENT_CN $CLIENT_CN" + echo "CLIENT_ALIAS $CLIENT_ALIAS" + echo "CLIENT_SELF_ALIAS $CLIENT_SELF_ALIAS" + cert_end_point + CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) + echo + done +fi echo echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" @@ -393,11 +348,13 @@ keytool \ -deststoretype pkcs12 \ -srcstorepass $SERVER_STORE_PWD -echo -echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" -keytool \ - -importkeystore \ - -srckeystore $CLIENT_STORE \ - -destkeystore $CLIENT_STORE \ - -deststoretype pkcs12 \ - -srcstorepass $CLIENT_STORE_PWD +if [ "$client_start" -lt "$client_finish" ]; then + echo + echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" + keytool \ + -importkeystore \ + -srckeystore $CLIENT_STORE \ + -destkeystore $CLIENT_STORE \ + -deststoretype pkcs12 \ + -srcstorepass $CLIENT_STORE_PWD +fi diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties index 18f851e4dd..7b3cd9c09a 100644 --- a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties +++ b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties @@ -41,9 +41,9 @@ BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed" # Client CLIENT_STORE=clientKeyStore1.jks CLIENT_STORE_PWD=client_ks_password1 -CLIENT_ALIAS=client_alias_1 -CLIENT_PREFIX=LwX509_ -CLIENT_SELF_ALIAS=client_self_signed +CLIENT_ALIAS_PREFIX=client_alias_1 +CLIENT_PREFIX=LwX509___ +CLIENT_SELF_ALIAS_PREFIX=client_self_signed_1 CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed" # Color output stuff diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index d81344fe5c..ea20f6fb50 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -51,6 +51,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCre import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -248,12 +249,25 @@ public class JsonConverter { } private static void parseNumericValue(List result, Entry valueEntry, JsonPrimitive value) { - if (value.getAsString().contains(".")) { - result.add(new DoubleDataEntry(valueEntry.getKey(), value.getAsDouble())); + String valueAsString = value.getAsString(); + String key = valueEntry.getKey(); + if (valueAsString.contains("e") || valueAsString.contains("E")) { + var bd = new BigDecimal(valueAsString); + if (bd.stripTrailingZeros().scale() <= 0) { + try { + result.add(new LongDataEntry(key, bd.longValueExact())); + } catch (ArithmeticException e) { + result.add(new DoubleDataEntry(key, bd.doubleValue())); + } + } else { + result.add(new DoubleDataEntry(key, bd.doubleValue())); + } + } else if (valueAsString.contains(".")) { + result.add(new DoubleDataEntry(key, value.getAsDouble())); } else { try { long longValue = Long.parseLong(value.getAsString()); - result.add(new LongDataEntry(valueEntry.getKey(), longValue)); + result.add(new LongDataEntry(key, longValue)); } catch (NumberFormatException e) { throw new JsonSyntaxException("Big integer values are not supported!"); } @@ -288,7 +302,8 @@ public class JsonConverter { return result; } - public static JsonObject getJsonObjectForGateway(String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) { + public static JsonObject getJsonObjectForGateway(String deviceName, TransportProtos.GetAttributeResponseMsg + responseMsg) { JsonObject result = new JsonObject(); result.addProperty("id", responseMsg.getRequestId()); result.addProperty(DEVICE_PROPERTY, deviceName); @@ -301,7 +316,8 @@ public class JsonConverter { return result; } - public static JsonObject getJsonObjectForGateway(String deviceName, AttributeUpdateNotificationMsg notificationMsg) { + public static JsonObject getJsonObjectForGateway(String deviceName, AttributeUpdateNotificationMsg + notificationMsg) { JsonObject result = new JsonObject(); result.addProperty(DEVICE_PROPERTY, deviceName); result.add("data", toJson(notificationMsg)); @@ -451,7 +467,8 @@ public class JsonConverter { return result; } - public static JsonElement toGatewayJson(String deviceName, TransportProtos.ProvisionDeviceResponseMsg responseRequest) { + public static JsonElement toGatewayJson(String deviceName, TransportProtos.ProvisionDeviceResponseMsg + responseRequest) { JsonObject result = new JsonObject(); result.addProperty(DEVICE_PROPERTY, deviceName); result.add("data", JsonConverter.toJson(responseRequest)); @@ -501,15 +518,18 @@ public class JsonConverter { return result; } - public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs) throws JsonSyntaxException { + public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs) throws + JsonSyntaxException { return convertToTelemetry(jsonElement, systemTs, false); } - public static Map> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws JsonSyntaxException { + public static Map> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws + JsonSyntaxException { return convertToTelemetry(jsonElement, systemTs, true); } - public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws JsonSyntaxException { + public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws + JsonSyntaxException { Map> result = sorted ? new TreeMap<>() : new HashMap<>(); convertToTelemetry(jsonElement, systemTs, result, null); return result; @@ -579,7 +599,8 @@ public class JsonConverter { .build(); } - private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String provisionKey, String provisionSecret) { + private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String + provisionKey, String provisionSecret) { return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder() .setProvisionDeviceKey(provisionKey) .setProvisionDeviceSecret(provisionSecret) diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java index 48f31edc97..14da88ecd1 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java @@ -39,49 +39,21 @@ public class LwM2MTransportConfigBootstrap { @Value("${transport.lwm2m.bootstrap.id:}") private Integer bootstrapServerId; - @Getter - @Value("${transport.lwm2m.bootstrap.secure.start_psk:}") - private Boolean bootstrapStartPsk; - - @Getter - @Value("${transport.lwm2m.bootstrap.secure.start_rpk:}") - private Boolean bootstrapStartRpk; - - @Getter - @Value("${transport.lwm2m.bootstrap.secure.start_x509:}") - private Boolean bootstrapStartX509; - @Getter @Value("${transport.lwm2m.bootstrap.bind_address:}") private String bootstrapHost; @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_address:}") - private String bootstrapSecureHost; - - @Getter - @Value("${transport.lwm2m.bootstrap.bind_port_no_sec_psk:}") - private Integer bootstrapPortNoSecPsk; - - @Getter - @Value("${transport.lwm2m.bootstrap.bind_port_no_sec_rpk:}") - private Integer bootstrapPortNoSecRpk; - - @Getter - @Value("${transport.lwm2m.bootstrap.bind_port_no_sec_x509:}") - private Integer bootstrapPortNoSecX509; - - @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_port_psk:}") - private Integer bootstrapSecurePortPsk; + @Value("${transport.lwm2m.bootstrap.bind_port_no_sec:}") + private Integer bootstrapPortNoSec; @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_port_rpk:}") - private Integer bootstrapSecurePortRpk; + @Value("${transport.lwm2m.bootstrap.secure.bind_address_security:}") + private String bootstrapHostSecurity; @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_port_x509:}") - private Integer bootstrapSecurePortX509; + @Value("${transport.lwm2m.bootstrap.secure.bind_port_security:}") + private Integer bootstrapPortSecurity; @Getter @Value("${transport.lwm2m.bootstrap.secure.public_x:}") @@ -96,8 +68,8 @@ public class LwM2MTransportConfigBootstrap { private PublicKey bootstrapPublicKey; @Getter - @Value("${transport.lwm2m.bootstrap.secure.private_s:}") - private String bootstrapPrivateS; + @Value("${transport.lwm2m.bootstrap.secure.private_encoded:}") + private String bootstrapPrivateEncoded; @Getter @Value("${transport.lwm2m.bootstrap.secure.alias:}") diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java index dfe2b75fef..248475a002 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java @@ -79,7 +79,7 @@ public class LwM2MTransportConfigServer { private String BASE_DIR_PATH = System.getProperty("user.dir"); @Getter -// private String PATH_DATA_MICROSERVICE = "/usr/share/tb-lwm2m-transport/data$"; + // private String PATH_DATA_MICROSERVICE = "/usr/share/tb-lwm2m-transport/data$"; private String PATH_DATA = "data"; @Getter @@ -147,57 +147,28 @@ public class LwM2MTransportConfigServer { private String rootAlias; @Getter - @Value("${transport.lwm2m.server.secure.start_psk:}") - private boolean serverStartPsk; - - @Getter - @Value("${transport.lwm2m.server.secure.start_rpk:}") - private boolean serverStartRpk; - - @Getter - @Value("${transport.lwm2m.server.secure.start_x509:}") - private boolean serverStartX509; - - @Getter - @Value("${transport.lwm2m.secure.enable_gen_psk_rpk:}") - private Boolean enableGenPskRpk; - - @Getter - @Value("${transport.lwm2m.server.bind_address:}") - private String serverHost; + @Value("${transport.lwm2m.secure.enable_gen_new_key_psk_rpk:}") + private Boolean enableGenNewKeyPskRpk; @Getter @Value("${transport.lwm2m.server.id:}") private Integer serverId; @Getter - @Value("${transport.lwm2m.server.secure.bind_address:}") - private String serverSecureHost; - - - @Getter - @Value("${transport.lwm2m.server.bind_port_no_sec_psk:}") - private Integer serverPortNoSecPsk; - - @Getter - @Value("${transport.lwm2m.server.bind_port_no_sec_rpk:}") - private Integer serverPortNoSecRpk; - - @Getter - @Value("${transport.lwm2m.server.bind_port_no_sec_x509:}") - private Integer serverPortNoSecX509; + @Value("${transport.lwm2m.server.bind_address:}") + private String serverHost; @Getter - @Value("${transport.lwm2m.server.secure.bind_port_psk:}") - private Integer serverPortPsk; + @Value("${transport.lwm2m.server.secure.bind_address_security:}") + private String serverHostSecurity; @Getter - @Value("${transport.lwm2m.server.secure.bind_port_rpk:}") - private Integer serverPortRpk; + @Value("${transport.lwm2m.server.bind_port_no_sec:}") + private Integer serverPortNoSec; @Getter - @Value("${transport.lwm2m.server.secure.bind_port_x509:}") - private Integer serverPortX509; + @Value("${transport.lwm2m.server.secure.bind_port_security:}") + private Integer serverPortSecurity; @Getter @Value("${transport.lwm2m.server.secure.public_x:}") @@ -208,8 +179,8 @@ public class LwM2MTransportConfigServer { private String serverPublicY; @Getter - @Value("${transport.lwm2m.server.secure.private_s:}") - private String serverPrivateS; + @Value("${transport.lwm2m.server.secure.private_encoded:}") + private String serverPrivateEncoded; @Getter @Value("${transport.lwm2m.server.secure.alias:}") @@ -303,7 +274,4 @@ public class LwM2MTransportConfigServer { ResourceModel resource = this.getResourceModel(registration, pathIds); return (resource == null) ? ResourceModel.Operations.NONE : resource.operations; } - - - } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 893a93e31a..473eeea42c 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -784,8 +784,8 @@ public class DefaultTransportService implements TransportService { wrappedCallback); } - protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator()); + private void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, tbMsg.getOriginator()); if (log.isTraceEnabled()) { log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, tbMsg.getOriginator(), tpi.getFullTopicName(), tbMsg); } @@ -797,7 +797,7 @@ public class DefaultTransportService implements TransportService { ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), wrappedCallback); } - protected void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, TransportProtos.SessionInfoProto sessionInfo, JsonObject json, + private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, TransportProtos.SessionInfoProto sessionInfo, JsonObject json, TbMsgMetaData metaData, SessionMsgType sessionMsgType, TbQueueCallback callback) { DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId); diff --git a/common/transport/transport-api/src/test/java/JsonConverterTest.java b/common/transport/transport-api/src/test/java/JsonConverterTest.java new file mode 100644 index 0000000000..cedbef50c9 --- /dev/null +++ b/common/transport/transport-api/src/test/java/JsonConverterTest.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2021 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. + */ + +import com.google.gson.JsonParser; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; + +@RunWith(MockitoJUnitRunner.class) +public class JsonConverterTest { + + private static final JsonParser JSON_PARSER = new JsonParser(); + + @Test + public void testParseBigDecimalAsLong() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1E+1}"), 0L); + Assert.assertEquals(10L, result.get(0L).get(0).getLongValue().get().longValue()); + } + + @Test + public void testParseBigDecimalAsDouble() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 101E-1}"), 0L); + Assert.assertEquals(10.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); + } + + @Test + public void testParseAsDouble() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1.1}"), 0L); + Assert.assertEquals(1.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); + } + + @Test + public void testParseAsLong() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 11}"), 0L); + Assert.assertEquals(11L, result.get(0L).get(0).getLongValue().get().longValue()); + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index b61e2d91ab..fc6ee959e0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -109,9 +109,10 @@ public class TbMsgGeneratorNode implements TbNode { } }, t -> { - if (initialized) { + if (initialized && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { ctx.tellFailure(msg, t); scheduleTickMsg(ctx); + currentMsgCount++; } }); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java index 1d5ae744c3..63b326a816 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java @@ -45,11 +45,13 @@ import java.util.concurrent.ConcurrentHashMap; @Slf4j @RuleNode(type = ComponentType.ENRICHMENT, - name = "calculate delta", + name = "calculate delta", relationTypes = {"Success", "Failure", "Other"}, configClazz = CalculateDeltaNodeConfiguration.class, nodeDescription = "Calculates and adds 'delta' value into message based on the incoming and previous value", nodeDetails = "Calculates delta and period based on the previous time-series reading and current data. " + - "Delta calculation is done in scope of the message originator, e.g. device, asset or customer.", + "Delta calculation is done in scope of the message originator, e.g. device, asset or customer. " + + "If there is input key, the output relation will be 'Success' unless delta is negative and corresponding configuration parameter is set. " + + "If there is no input value key in the incoming message, the output relation will be 'Other'.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeCalculateDeltaConfig") public class CalculateDeltaNode implements TbNode { @@ -93,12 +95,17 @@ public class CalculateDeltaNode implements TbNode { return; } + if (config.getRound() != null) { delta = delta.setScale(config.getRound(), RoundingMode.HALF_UP); } ObjectNode result = (ObjectNode) json; - result.put(config.getOutputValueKey(), delta); + if (delta.stripTrailingZeros().scale() > 0) { + result.put(config.getOutputValueKey(), delta.doubleValue()); + } else { + result.put(config.getOutputValueKey(), delta.longValueExact()); + } if (config.isAddPeriodBetweenMsgs()) { long period = previousData != null ? currentTs - previousData.ts : 0; @@ -107,13 +114,11 @@ public class CalculateDeltaNode implements TbNode { ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), JacksonUtil.toString(result))); }, t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); - } else if (config.isTellFailureIfInputValueKeyIsAbsent()) { - ctx.tellNext(msg, TbRelationTypes.FAILURE); } else { - ctx.tellSuccess(msg); + ctx.tellNext(msg, "Other"); } } else { - ctx.tellSuccess(msg); + ctx.tellNext(msg, "Other"); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java index b16a97caec..bec93a1d90 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java @@ -15,10 +15,12 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; @Data +@JsonIgnoreProperties(ignoreUnknown = true) public class CalculateDeltaNodeConfiguration implements NodeConfiguration { private String inputValueKey; private String outputValueKey; @@ -26,7 +28,6 @@ public class CalculateDeltaNodeConfiguration implements NodeConfiguration> listListenableFutureWithLess = + Futures.immediateFuture(Collections.singletonList(entry)); + + KeyFilter lowTempFilter = new KeyFilter(); + lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); + lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); + lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + lowTempPredicate.setValue( + new FilterPredicateValue<>( + 0.0, + null, + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "tenantAttribute", true)) + ); + lowTempFilter.setPredicate(lowTempPredicate); + AlarmCondition alarmCondition = new AlarmCondition(); + alarmCondition.setCondition(Collections.singletonList(lowTempFilter)); + AlarmRule alarmRule = new AlarmRule(); + alarmRule.setCondition(alarmCondition); + DeviceProfileAlarm dpa = new DeviceProfileAlarm(); + dpa.setId("lesstempID"); + dpa.setAlarmType("lessTemperatureAlarm"); + dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); + + deviceProfileData.setAlarms(Collections.singletonList(dpa)); + deviceProfile.setProfileData(deviceProfileData); - UUID uuid = new UUID(6041557255264276971L, -9019477126543226049L); - System.out.println(uuid); + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) + .thenReturn(Futures.immediateFuture(Collections.emptyList())); + Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) + .thenReturn(Futures.immediateFuture(null)); + Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())) + .thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); + Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) + .thenReturn(listListenableFutureWithLess); + + TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); + Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) + .thenReturn(theMsg); + + ObjectNode data = mapper.createObjectNode(); + data.put("temperature", 150L); + TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); + + node.onMsg(ctx, msg); + verify(ctx).tellSuccess(msg); + verify(ctx).tellNext(theMsg, "Alarm Created"); + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); + + } + + @Test + public void testCustomerInheritModeForDynamicValues() throws Exception { + init(); + DeviceProfile deviceProfile = new DeviceProfile(); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + + AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey( + EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "customerAttribute" + ); + + AttributeKvEntity attributeKvEntity = new AttributeKvEntity(); + attributeKvEntity.setId(compositeKey); + attributeKvEntity.setLongValue(100L); + attributeKvEntity.setLastUpdateTs(0L); + + AttributeKvEntry entry = attributeKvEntity.toData(); + ListenableFuture> listListenableFutureWithLess = + Futures.immediateFuture(Collections.singletonList(entry)); + ListenableFuture> optionalListenableFutureWithLess = + Futures.immediateFuture(Optional.of(entry)); + + KeyFilter lowTempFilter = new KeyFilter(); + lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); + lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); + lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + lowTempPredicate.setValue( + new FilterPredicateValue<>( + 0.0, + null, + new DynamicValue<>(DynamicValueSourceType.CURRENT_CUSTOMER, "customerAttribute", true)) + ); + lowTempFilter.setPredicate(lowTempPredicate); + AlarmCondition alarmCondition = new AlarmCondition(); + alarmCondition.setCondition(Collections.singletonList(lowTempFilter)); + AlarmRule alarmRule = new AlarmRule(); + alarmRule.setCondition(alarmCondition); + DeviceProfileAlarm dpa = new DeviceProfileAlarm(); + dpa.setId("lesstempID"); + dpa.setAlarmType("lessTemperatureAlarm"); + dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); + + deviceProfileData.setAlarms(Collections.singletonList(dpa)); + deviceProfile.setProfileData(deviceProfileData); + + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) + .thenReturn(Futures.immediateFuture(Collections.emptyList())); + Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) + .thenReturn(Futures.immediateFuture(null)); + Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())) + .thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); + Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) + .thenReturn(listListenableFutureWithLess); + Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) + .thenReturn(optionalListenableFutureWithLess); + + TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); + Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) + .thenReturn(theMsg); + + ObjectNode data = mapper.createObjectNode(); + data.put("temperature", 150L); + TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); + + node.onMsg(ctx, msg); + verify(ctx).tellSuccess(msg); + verify(ctx).tellNext(theMsg, "Alarm Created"); + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); + } + + private void init() throws TbNodeException { Mockito.when(ctx.getTenantId()).thenReturn(tenantId); Mockito.when(ctx.getDeviceProfileCache()).thenReturn(cache); Mockito.when(ctx.getTimeseriesService()).thenReturn(timeseriesService); diff --git a/transport/lwm2m/src/main/data/credentials/serverKeyStore.jks b/transport/lwm2m/src/main/data/credentials/serverKeyStore.jks index 5fcb65d351..9f6748f8fd 100644 Binary files a/transport/lwm2m/src/main/data/credentials/serverKeyStore.jks and b/transport/lwm2m/src/main/data/credentials/serverKeyStore.jks differ diff --git a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh index 3167cca2b5..dcf38b4f10 100755 --- a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh +++ b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Copyright © 2016-2021 The Thingsboard Authors # @@ -15,77 +15,35 @@ # limitations under the License. # -#p) CLIENT_CN=LwX50900000000 +#/home/nick/Igor_project/Thingsboard_Perfrmance_test/performance-tests/src/main/resources/credentials/shell/lwM2M_credentials.sh -p LwX509 -s 0 -f 2000 -a client_alias_ -e client_self_signed_ -b bootstrap -d server -j serverKeyStore.jks -k clientKeyStore.jks -c client_ks_password -w server_ks_password + +#p) CLIENT_CN=$CLIENT_PREFIX00000000 #s) client_start=0 #f) client_finish=1 -#a) CLIENT_ALIAS=client_alias_00000000 +#a) CLIENT_ALIAS=CLIENT_ALIAS_PREFIX_00000000 +#e) CLIENT_SELF_ALIAS=CLIENT_SELF_ALIAS_PREFIX_00000000 #b) BOOTSTRAP_ALIAS=bootstrap #d) SERVER_ALIAS=server #j) SERVER_STORE=serverKeyStore.jks #k) CLIENT_STORE=clientKeyStore.jks #c) CLIENT_STORE_PWD=client_ks_password #w) SERVER_STORE_PWD=server_ks_password +#l) ROOT_KEY_ALIAS=root_key_alias -#while test $# -gt 0; do -# case "$1" in -# -h|--help) -# echo "$package - attempt to capture frames" -# echo " " -# echo "$package [options] application [arguments]" -# echo " " -# echo "options:" -# echo "-h, --help show brief help" -# echo "-a, --action=ACTION specify an action to use" -# echo "-o, --output-dir=DIR specify a directory to store output in" -# exit 0 -# ;; -# -a) -# shift -# if test $# -gt 0; then -# export PROCESS=$1 -# else -# echo "no process specified" -# exit 1 -# fi -# shift -# ;; -# --action*) -# export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'` -# shift -# ;; -# -o) -# shift -# if test $# -gt 0; then -# export OUTPUT=$1 -# else -# echo "no output dir specified" -# exit 1 -# fi -# shift -# ;; -# --output-dir*) -# export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'` -# shift -# ;; -# *) -# break -# ;; -# esac -#done - - -while getopts p:s:f:a:b:d:j:k:c:w: flag; do +while getopts p:s:f:a:e:b:d:j:k:c:w:l: flag; do case "${flag}" in - p) client_prefix=${OPTARG} ;; + p) client_pref=${OPTARG} ;; s) client_start=${OPTARG} ;; f) client_finish=${OPTARG} ;; - a) client_alias=${OPTARG} ;; + a) client_alias_pref=${OPTARG} ;; + e) client_self_alias_pref=${OPTARG} ;; b) bootstrap_alias=${OPTARG} ;; d) server_alias=${OPTARG} ;; j) key_store_server_file=${OPTARG} ;; k) key_store_client_file=${OPTARG} ;; c) client_key_store_pwd=${OPTARG} ;; w) server_key_store_pwd=${OPTARG} ;; + w) root_key_alias=${OPTARG} ;; esac done @@ -96,9 +54,8 @@ cd $script_dir # source the properties: . ./lwM2M_keygen.properties - -if [ -n "$client_prefix" ]; then - CLIENT_PREFIX=$client_prefix +if [ -n "$client_pref" ]; then + CLIENT_PREFIX=$client_pref fi if [ -z "$client_start" ]; then @@ -109,8 +66,12 @@ if [ -z "$client_finish" ]; then client_finish=1 fi -if [ -n "$client_alias" ]; then - CLIENT_ALIAS=$client_alias +if [ -n "$client_alias_pref" ]; then + CLIENT_ALIAS_PREFIX=$client_alias_pref +fi + +if [ -n "$client_self_alias_pref" ]; then + CLIENT_SELF_ALIAS_PREFIX=$client_self_alias_pref fi if [ -n "$bootstrap_alias" ]; then @@ -137,23 +98,37 @@ if [ -n "$server_key_store_pwd" ]; then SERVER_STORE_PWD=$server_key_store_pwd fi +if [ -n "$root_key_alias" ]; then + ROOT_KEY_ALIAS=$root_key_alias +fi + +CLIENT_NUMBER=$client_start + echo "==Start==" echo "CLIENT_PREFIX: $CLIENT_PREFIX" echo "client_start: $client_start" echo "client_finish: $client_finish" -echo "CLIENT_ALIAS: $CLIENT_ALIAS" +echo "CLIENT_ALIAS_PREFIX: $CLIENT_ALIAS_PREFIX" +echo "CLIENT_SELF_ALIAS_PREFIX: $CLIENT_SELF_ALIAS_PREFIX" echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS" echo "SERVER_ALIAS: $SERVER_ALIAS" echo "SERVER_STORE: $SERVER_STORE" echo "CLIENT_STORE: $CLIENT_STORE" echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD" echo "SERVER_STORE_PWD: $SERVER_STORE_PWD" +echo "CLIENT_NUMBER: $CLIENT_NUMBER" +echo "ROOT_KEY_ALIAS: $ROOT_KEY_ALIAS" end_point() { echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)" } + client_alias_point() { - echo "$CLIENT_ALIAS$(printf "%08d" $CLIENT_NUMBER)" + echo "$CLIENT_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" +} + +client_self_alias_point() { + echo "$CLIENT_SELF_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)" } # Generation of the keystore. @@ -264,73 +239,30 @@ keytool \ -keystore $SERVER_STORE \ -storepass $SERVER_STORE_PWD -echo -echo "${H1}Client Keystore : ${RESET}" -echo "${H1}==================${RESET}" -#echo "${H2}Creating client key and self-signed certificate with expected CN...${RESET}" -#keytool \ -# -genkeypair \ -# -alias $CLIENT_ALIAS \ -# -keyalg EC \ -# -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -# -validity $VALIDITY \ -# -storetype $STORETYPE \ -# -keypass $CLIENT_STORE_PWD \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD -#keytool \ -# -exportcert \ -# -alias $CLIENT_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD | \ -# keytool \ -# -importcert \ -# -alias $CLIENT_SELF_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD \ -# -noprompt - -echo -echo "${H2}Import root certificate just to be able to import need by root CA with expected CN...${RESET}" -keytool \ - -exportcert \ - -alias $ROOT_KEY_ALIAS \ - -keystore $SERVER_STORE \ - -storepass $SERVER_STORE_PWD | +if [ "$client_start" -lt "$client_finish" ]; then + echo + echo "${H2}Import root certificate just to be able to import need by root CA with expected CN to $CLIENT_STORE${RESET}" keytool \ - -importcert \ + -exportcert \ -alias $ROOT_KEY_ALIAS \ - -keystore $CLIENT_STORE \ - -storepass $CLIENT_STORE_PWD \ - -noprompt - -#echo -#echo "${H2}Creating client certificate signed by root CA with expected CN...${RESET}" -#keytool \ -# -certreq \ -# -alias $CLIENT_ALIAS \ -# -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD | \ -# keytool \ -# -gencert \ -# -alias $ROOT_KEY_ALIAS \ -# -keystore $SERVER_STORE \ -# -storepass $SERVER_STORE_PWD \ -# -storetype $STORETYPE \ -# -validity $VALIDITY | \ -# keytool \ -# -importcert \ -# -alias $CLIENT_ALIAS \ -# -keystore $CLIENT_STORE \ -# -storepass $CLIENT_STORE_PWD \ -# -noprompt + -keystore $SERVER_STORE \ + -storepass $SERVER_STORE_PWD | + keytool \ + -importcert \ + -alias $ROOT_KEY_ALIAS \ + -keystore $CLIENT_STORE \ + -storepass $CLIENT_STORE_PWD \ + -noprompt +fi cert_end_point() { - echo "${H2}Creating client key and self-signed certificate with expected CN $CLIENT_SELF_CN ${RESET}" + echo + echo "${H1}Client Keystore : ${RESET}" + echo "${H1}==================${RESET}" + echo "${H2}Creating client key and self-signed certificate with expected CN CLIENT_ALIAS: $CLIENT_ALIAS${RESET}" keytool \ -genkeypair \ - -alias $CLIENT_CN_ALIAS \ + -alias $CLIENT_ALIAS \ -keyalg EC \ -dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -validity $VALIDITY \ @@ -340,7 +272,7 @@ cert_end_point() { -storepass $CLIENT_STORE_PWD keytool \ -exportcert \ - -alias $CLIENT_CN_ALIAS \ + -alias $CLIENT_ALIAS \ -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD | keytool \ @@ -349,13 +281,28 @@ cert_end_point() { -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD \ -noprompt +# +# echo +# echo "${H2}Import root certificate just to be able to import ned by root CA with expected CN...${RESET}" +# keytool \ +# -exportcert \ +# -alias $ROOT_KEY_ALIAS \ +# -keystore $SERVER_STORE \ +# -storepass $SERVER_STORE_PWD | +# keytool \ +# -importcert \ +# -alias $ROOT_KEY_ALIAS \ +# -keystore $CLIENT_STORE \ +# -storepass $CLIENT_STORE_PWD \ +# -noprompt +# echo - echo "${H2}Creating client certificate signed by root CA with expected $CLIENT_CN_NAME ${RESET}" + echo "${H2}Creating client certificate signed by root CA with expected CN CLIENT_ALIAS: $CLIENT_ALIAS CLIENT_CN: $CLIENT_CN${RESET}" keytool \ -certreq \ - -alias $CLIENT_CN_ALIAS \ - -dname "CN=$CLIENT_CN_NAME, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ + -alias $CLIENT_ALIAS \ + -dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD | keytool \ @@ -367,22 +314,30 @@ cert_end_point() { -validity $VALIDITY | keytool \ -importcert \ - -alias $CLIENT_CN_ALIAS \ + -alias $CLIENT_ALIAS \ -keystore $CLIENT_STORE \ -storepass $CLIENT_STORE_PWD \ -noprompt } -while [ "$CLIENT_NUMBER" != "$client_finish" ]; do - CLIENT_CN_NAME=$(end_point) - CLIENT_CN_ALIAS=$(client_alias_point) - echo "$CLIENT_CN_NAME" - echo "$CLIENT_CN_ALIAS" - cert_end_point - CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) - echo "number $CLIENT_NUMBER" - echo "finish $client_finish" -done +if [ "$client_start" -lt "$client_finish" ]; then + echo "Файл содержит, как минимум, одно слово Bash." + echo + echo "==Start Client==" + while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do + echo "number $CLIENT_NUMBER" + echo "finish $client_finish" + CLIENT_CN=$(end_point) + CLIENT_ALIAS=$(client_alias_point) + CLIENT_SELF_ALIAS=$(client_self_alias_point) + echo "CLIENT_CN $CLIENT_CN" + echo "CLIENT_ALIAS $CLIENT_ALIAS" + echo "CLIENT_SELF_ALIAS $CLIENT_SELF_ALIAS" + cert_end_point + CLIENT_NUMBER=$(($CLIENT_NUMBER + 1)) + echo + done +fi echo echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" @@ -393,11 +348,13 @@ keytool \ -deststoretype pkcs12 \ -srcstorepass $SERVER_STORE_PWD -echo -echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" -keytool \ - -importkeystore \ - -srckeystore $CLIENT_STORE \ - -destkeystore $CLIENT_STORE \ - -deststoretype pkcs12 \ - -srcstorepass $CLIENT_STORE_PWD +if [ "$client_start" -lt "$client_finish" ]; then + echo + echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}" + keytool \ + -importkeystore \ + -srckeystore $CLIENT_STORE \ + -destkeystore $CLIENT_STORE \ + -deststoretype pkcs12 \ + -srcstorepass $CLIENT_STORE_PWD +fi diff --git a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties index 18f851e4dd..7b3cd9c09a 100644 --- a/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties +++ b/transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties @@ -41,9 +41,9 @@ BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed" # Client CLIENT_STORE=clientKeyStore1.jks CLIENT_STORE_PWD=client_ks_password1 -CLIENT_ALIAS=client_alias_1 -CLIENT_PREFIX=LwX509_ -CLIENT_SELF_ALIAS=client_self_signed +CLIENT_ALIAS_PREFIX=client_alias_1 +CLIENT_PREFIX=LwX509___ +CLIENT_SELF_ALIAS_PREFIX=client_self_signed_1 CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed" # Color output stuff diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 700b428906..3c6c9b2afa 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -53,14 +53,14 @@ transport: # model_path_file: "${LWM2M_MODEL_PATH_FILE:./common/transport/lwm2m/src/main/resources/models/}" model_path_file: "${LWM2M_MODEL_PATH_FILE:}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" - recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:false}" + recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" request_pool_size: "${LWM2M_REQUEST_POOL_SIZE:100}" request_error_pool_size: "${LWM2M_REQUEST_ERROR_POOL_SIZE:10}" registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" secure: - # Only Certificate_x509: + # Certificate_x509: # To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format # Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}" @@ -69,51 +69,42 @@ transport: key_store_path_file: "${KEY_STORE_PATH_FILE:}" key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}" root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" - enable_gen_psk_rpk: "${ENABLE_GEN_PSK_RPK:true}" + enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}" server: id: "${LWM2M_SERVER_ID:123}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}" - bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}" - bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}" + bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}" secure: - bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - start_psk: "${START_SERVER_PSK:true}" - start_rpk: "${START_SERVER_RPK:true}" - start_x509: "${START_SERVER_X509:true}" - bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}" - bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}" - bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}" - # Only RPK: Public & Private Key - # create_rpk: "${CREATE_RPK:}" - public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}" - public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}" - private_s: "${LWM2M_SERVER_PRIVATE_S:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}" - # Only Certificate_x509: + bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}" + bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}" + # create_rpk: "${CREATE_RPK:}" + # Only for RPK: Public & Private Key. If the keystore file is missing or not working + # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d0301070342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b] + # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b], + # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)] + public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}" + public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" + private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" # Only Certificate_x509: alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" bootstrap: - enable: "${BOOTSTRAP:true}" + enable: "${LWM2M_BOOTSTRAP_ENABLED:true}" id: "${LWM2M_SERVER_ID:111}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}" - bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}" - bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}" + bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}" secure: - bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - start_psk: "${START_SERVER_PSK_BS:true}" - start_rpk: "${START_SERVER_RPK_BS:true}" - start_x509: "${START_SERVER_X509_BS:true}" - bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}" - bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}" - bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}" - # Only RPK: Public & Private Key - public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}" - public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}" - private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}" - # Only Certificate_x509: + bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" + bind_port_security: "${LWM2M_BIND_PORT_SEC_BS:5688}" + # Only for RPK: Public & Private Key. If the keystore file is missing or not working + # - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)] + # - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d030107034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34] + # - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34], + public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}" + public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" + private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" # Only Certificate_x509: alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}" - # Redis + # Redis redis_url: "${LWM2M_REDIS_URL:''}" + sessions: inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:30000}" diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts index db12d8ff65..743b49c286 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts @@ -43,6 +43,7 @@ import { AlarmDetailsDialogComponent, AlarmDetailsDialogData } from '@home/components/alarm/alarm-details-dialog.component'; +import { DAY, historyInterval } from '@shared/models/time/time.models'; export class AlarmTableConfig extends EntityTableConfig { @@ -59,6 +60,7 @@ export class AlarmTableConfig extends EntityTableConfig this.loadDataOnInit = false; this.tableTitle = ''; this.useTimePageLink = true; + this.defaultTimewindowInterval = historyInterval(DAY * 30); this.detailsPanelEnabled = false; this.selectionEnabled = false; this.searchEnabled = true; diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index dedaced162..da735c1567 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -55,11 +55,11 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models'; import { DialogService } from '@core/services/dialog.service'; import { AddEntityDialogComponent } from './add-entity-dialog.component'; import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; -import { DAY, historyInterval, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; +import { HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { isDefined, isUndefined } from '@core/utils'; -import { HasUUID } from '../../../../shared/models/id/has-uuid'; +import { HasUUID } from '@shared/models/id/has-uuid'; @Component({ selector: 'tb-entities-table', @@ -202,7 +202,7 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; if (this.entitiesTableConfig.useTimePageLink) { - this.timewindow = historyInterval(DAY); + this.timewindow = this.entitiesTableConfig.defaultTimewindowInterval; const currentTime = Date.now(); this.pageLink = new TimePageLink(10, 0, null, sortOrder, currentTime - this.timewindow.history.timewindowMs, currentTime); @@ -446,7 +446,7 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn resetSortAndFilter(update: boolean = true, preserveTimewindow: boolean = false) { this.pageLink.textSearch = null; if (this.entitiesTableConfig.useTimePageLink && !preserveTimewindow) { - this.timewindow = historyInterval(DAY); + this.timewindow = this.entitiesTableConfig.defaultTimewindowInterval; } if (this.displayPagination) { this.paginator.pageIndex = 0; diff --git a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html index 6194581625..253f7e2138 100644 --- a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html @@ -16,7 +16,7 @@ -->
- + @@ -25,7 +25,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 41c04ea238..c6aee52976 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -26,12 +26,12 @@
-
+
- +
diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html index adda4bea1a..dd1b92cf7f 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html @@ -47,7 +47,7 @@
-
+
@@ -68,6 +68,14 @@
filter.source-attribute
+
+ + {{ 'filter.inherit-owner' | translate}} + +
filter.source-attribute-not-set
+