Browse Source

Merge branch 'develop/3.3' of github.com:thingsboard/thingsboard into develop/3.3-edge

pull/3811/head
Volodymyr Babak 6 years ago
parent
commit
10d3dd01bb
  1. 13
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  2. 6
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  3. 7
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  4. 8
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  5. 37
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java
  6. 5
      application/src/main/resources/logback.xml
  7. 64
      application/src/main/resources/thingsboard.yml
  8. 12
      common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java
  9. 37
      common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatisticConfig.java
  10. 184
      common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerStatsService.java
  11. 16
      common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java
  12. 11
      common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java
  13. 9
      common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java
  14. 7
      common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java
  15. 6
      common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java
  16. 313
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java
  17. 26
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerInitializer.java
  18. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java
  19. 392
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java
  20. 57
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java
  21. 10
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java
  22. BIN
      common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks
  23. 247
      common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
  24. 6
      common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties
  25. 41
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
  26. 44
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java
  27. 58
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java
  28. 6
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
  29. 53
      common/transport/transport-api/src/test/java/JsonConverterTest.java
  30. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java
  31. 19
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java
  32. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java
  33. 16
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java
  34. 147
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java
  35. BIN
      transport/lwm2m/src/main/data/credentials/serverKeyStore.jks
  36. 247
      transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh
  37. 6
      transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties
  38. 63
      transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
  39. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts
  40. 8
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts
  41. 4
      ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html
  42. 4
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html
  43. 10
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html
  44. 25
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts
  45. 4
      ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html
  46. 4
      ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html
  47. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts
  48. 4
      ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
  49. 1
      ui-ngx/src/app/shared/models/query/query.models.ts
  50. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

13
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);
}
}
}
}

6
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);
}

7
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);
}

8
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);
}
}
}

37
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

5
application/src/main/resources/logback.xml

@ -34,6 +34,9 @@
<!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>-->
<!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>-->
<!-- <logger name="org.eclipse.californium.scandium.DTLSConnector" level="TRACE" />-->
<!-- <logger name="org.eclipse.californium.scandium.dtls.Handshaker" level="DEBUG" />-->
<logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" />
<logger name="org.thingsboard.server.service.edge" level="INFO" />
@ -43,4 +46,4 @@
</root>
</configuration>
</configuration>

64
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}"

12
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<T> 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;
}
}

37
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;
}

184
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<String> monitoredGroups = ConcurrentHashMap.newKeySet();
private final TbKafkaSettings kafkaSettings;
private final TbKafkaConsumerStatisticConfig statsConfig;
private final PartitionService partitionService;
private AdminClient adminClient;
private Consumer<String, byte[]> 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<TopicPartition, OffsetAndMetadata> groupOffsets = adminClient.listConsumerGroupOffsets(groupId).partitionsToOffsetAndMetadata()
.get(statsConfig.getKafkaResponseTimeoutMs(), TimeUnit.MILLISECONDS);
Map<TopicPartition, Long> endOffsets = consumer.endOffsets(groupOffsets.keySet(), timeoutDuration);
List<GroupTopicStats> 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<GroupTopicStats> getTopicsStatsWithLag(Map<TopicPartition, OffsetAndMetadata> groupOffsets, Map<TopicPartition, Long> endOffsets) {
List<GroupTopicStats> 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 + "]" +
"]";
}
}
}

16
common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java

@ -42,10 +42,13 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue
private final KafkaConsumer<String, byte[]> consumer;
private final TbKafkaDecoder<T> decoder;
private final TbKafkaConsumerStatsService statsService;
private final String groupId;
@Builder
private TbKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> 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<T extends TbQueueMsg> 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<T extends TbQueueMsg> extends AbstractTbQue
consumer.unsubscribe();
consumer.close();
}
if (statsService != null) {
statsService.unregisterClientGroup(groupId);
}
}
}

11
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();
}

9
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
<TbProtoJsQueueMsg<JsInvokeProtos.RemoteJsRequest>, TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> 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();
}

7
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
<TbProtoJsQueueMsg<JsInvokeProtos.RemoteJsRequest>, TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> builder = DefaultTbQueueRequestTemplate.builder();

6
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<TransportApiRequestMsg>, TbProtoQueueMsg<TransportApiResponseMsg>> 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();
}

313
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);
}
}

26
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!");
}
}

16
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());
}
}
}

392
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> 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);
}
}

57
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!");
}
}

10
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<String> 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;
}

BIN
common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks

Binary file not shown.

247
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

6
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

41
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<KvEntry> result, Entry<String, JsonElement> 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<Long, List<KvEntry>> convertToTelemetry(JsonElement jsonElement, long systemTs) throws JsonSyntaxException {
public static Map<Long, List<KvEntry>> convertToTelemetry(JsonElement jsonElement, long systemTs) throws
JsonSyntaxException {
return convertToTelemetry(jsonElement, systemTs, false);
}
public static Map<Long, List<KvEntry>> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws JsonSyntaxException {
public static Map<Long, List<KvEntry>> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws
JsonSyntaxException {
return convertToTelemetry(jsonElement, systemTs, true);
}
public static Map<Long, List<KvEntry>> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws JsonSyntaxException {
public static Map<Long, List<KvEntry>> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws
JsonSyntaxException {
Map<Long, List<KvEntry>> 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)

44
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:}")

58
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;
}
}

6
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);

53
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());
}
}

3
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++;
}
});
}

19
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");
}
}

4
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<CalculateDeltaNodeConfiguration> {
private String inputValueKey;
private String outputValueKey;
@ -26,7 +28,6 @@ public class CalculateDeltaNodeConfiguration implements NodeConfiguration<Calcul
private boolean addPeriodBetweenMsgs;
private String periodValueKey;
private Integer round;
private boolean tellFailureIfInputValueKeyIsAbsent;
private boolean tellFailureIfDeltaIsNegative;
@Override
@ -37,7 +38,6 @@ public class CalculateDeltaNodeConfiguration implements NodeConfiguration<Calcul
configuration.setUseCache(true);
configuration.setAddPeriodBetweenMsgs(false);
configuration.setPeriodValueKey("periodInMs");
configuration.setTellFailureIfInputValueKeyIsAbsent(true);
configuration.setTellFailureIfDeltaIsNegative(true);
return configuration;
}

16
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java

@ -388,12 +388,6 @@ class AlarmRuleState {
EntityKeyValue ekv = null;
if (value.getDynamicValue() != null) {
switch (value.getDynamicValue().getSourceType()) {
case CURRENT_TENANT:
ekv = dynamicPredicateValueCtx.getTenantValue(value.getDynamicValue().getSourceAttribute());
break;
case CURRENT_CUSTOMER:
ekv = dynamicPredicateValueCtx.getCustomerValue(value.getDynamicValue().getSourceAttribute());
break;
case CURRENT_DEVICE:
ekv = data.getValue(new EntityKey(EntityKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
if (ekv == null) {
@ -405,6 +399,16 @@ class AlarmRuleState {
}
}
}
if(ekv != null || !value.getDynamicValue().isInherit()) {
break;
}
case CURRENT_CUSTOMER:
ekv = dynamicPredicateValueCtx.getCustomerValue(value.getDynamicValue().getSourceAttribute());
if(ekv != null || !value.getDynamicValue().isInherit()) {
break;
}
case CURRENT_TENANT:
ekv = dynamicPredicateValueCtx.getTenantValue(value.getDynamicValue().getSourceAttribute());
}
}
return ekv;

147
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java

@ -434,11 +434,152 @@ public class TbDeviceProfileNodeTest {
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
}
private void init() throws TbNodeException {
@Test
public void testTenantInheritModeForDynamicValues() throws Exception {
init();
DeviceProfile deviceProfile = new DeviceProfile();
DeviceProfileData deviceProfileData = new DeviceProfileData();
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey(
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "tenantAttribute"
);
AttributeKvEntity attributeKvEntity = new AttributeKvEntity();
attributeKvEntity.setId(compositeKey);
attributeKvEntity.setLongValue(100L);
attributeKvEntity.setLastUpdateTs(0L);
AttributeKvEntry entry = attributeKvEntity.toData();
ListenableFuture<List<AttributeKvEntry>> 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<List<AttributeKvEntry>> listListenableFutureWithLess =
Futures.immediateFuture(Collections.singletonList(entry));
ListenableFuture<Optional<AttributeKvEntry>> 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);

BIN
transport/lwm2m/src/main/data/credentials/serverKeyStore.jks

Binary file not shown.

247
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

6
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

63
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}"

2
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<AlarmInfo, TimePageLink> {
@ -59,6 +60,7 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
this.loadDataOnInit = false;
this.tableTitle = '';
this.useTimePageLink = true;
this.defaultTimewindowInterval = historyInterval(DAY * 30);
this.detailsPanelEnabled = false;
this.selectionEnabled = false;
this.searchEnabled = true;

8
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;

4
ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html

@ -16,7 +16,7 @@
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="booleanFilterPredicateFormGroup">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="30" class="mat-block">
<mat-label></mat-label>
<mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
<mat-option *ngFor="let operation of booleanOperations" [value]="operation">
@ -25,7 +25,7 @@
</mat-select>
</mat-form-field>
<tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
fxFlex="60"
fxFlex="70"
[valueType]="valueTypeEnum.BOOLEAN"
formControlName="value">
</tb-filter-predicate-value>

4
ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html

@ -26,12 +26,12 @@
<span fxFlex="8"></span>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" fxFlex="92">
<div fxFlex fxLayout="row" fxLayoutGap="8px">
<div fxFlex="40" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div fxFlex="30" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<label fxFlex translate class="tb-title no-padding">filter.operation.operation</label>
<label *ngIf="valueType === valueTypeEnum.STRING"
translate class="tb-title no-padding" style="min-width: 70px;">filter.ignore-case</label>
</div>
<label fxFlex="60" translate class="tb-title no-padding">filter.value</label>
<label fxFlex="70" translate class="tb-title no-padding">filter.value</label>
</div>
<label *ngIf="displayUserParameters"
translate class="tb-title no-padding" style="width: 60px;">filter.user-parameters</label>

10
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html

@ -47,7 +47,7 @@
</div>
<div fxFlex fxLayout="column" [fxShow]="dynamicMode">
<div formGroupName="dynamicValue" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div fxFlex fxLayout="column">
<div fxFlex="35" fxLayout="column">
<mat-form-field floatLabel="always" hideRequiredMarker class="mat-block">
<mat-label></mat-label>
<mat-select formControlName="sourceType" placeholder="{{'filter.dynamic-source-type' | translate}}">
@ -68,6 +68,14 @@
</mat-form-field>
<div class="tb-hint" translate>filter.source-attribute</div>
</div>
<div *ngIf="!allow && inheritMode"
fxLayout="column"
style="padding-top: 6px">
<mat-checkbox formControlName="inherit">
{{ 'filter.inherit-owner' | translate}}
</mat-checkbox>
<div class="tb-hint" translate>filter.source-attribute-not-set</div>
</div>
</div>
</div>
<button mat-icon-button

25
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts

@ -44,6 +44,10 @@ import {
})
export class FilterPredicateValueComponent implements ControlValueAccessor, OnInit {
private readonly inheritModeForSources: DynamicValueSourceType[] = [
DynamicValueSourceType.CURRENT_CUSTOMER,
DynamicValueSourceType.CURRENT_DEVICE];
@Input() disabled: boolean;
@Input()
@ -72,6 +76,8 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
dynamicMode = false;
inheritMode = false;
allow = true;
private propagateChange = null;
@ -105,7 +111,8 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
dynamicValue: this.fb.group(
{
sourceType: [null],
sourceAttribute: [null]
sourceAttribute: [null],
inherit: [false]
}
)
});
@ -114,6 +121,7 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
if (!sourceType) {
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false});
}
this.updateShowInheritMode(sourceType);
}
);
this.filterPredicateValueFormGroup.valueChanges.subscribe(() => {
@ -139,10 +147,13 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
writeValue(predicateValue: FilterPredicateValue<string | number | boolean>): void {
this.filterPredicateValueFormGroup.get('defaultValue').patchValue(predicateValue.defaultValue, {emitEvent: false});
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').patchValue(predicateValue.dynamicValue ?
this.filterPredicateValueFormGroup.get('dynamicValue.sourceType').patchValue(predicateValue.dynamicValue ?
predicateValue.dynamicValue.sourceType : null, {emitEvent: false});
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(predicateValue.dynamicValue ?
this.filterPredicateValueFormGroup.get('dynamicValue.sourceAttribute').patchValue(predicateValue.dynamicValue ?
predicateValue.dynamicValue.sourceAttribute : null, {emitEvent: false});
this.filterPredicateValueFormGroup.get('dynamicValue.inherit').patchValue(predicateValue.dynamicValue ?
predicateValue.dynamicValue.inherit : false, {emitEvent: false});
this.updateShowInheritMode(predicateValue?.dynamicValue?.sourceType);
}
private updateModel() {
@ -158,4 +169,12 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
this.propagateChange(predicateValue);
}
private updateShowInheritMode(sourceType: DynamicValueSourceType) {
if (this.inheritModeForSources.includes(sourceType)) {
this.inheritMode = true;
} else {
this.filterPredicateValueFormGroup.get('dynamicValue.inherit').patchValue(false, {emitEvent: false});
this.inheritMode = false;
}
}
}

4
ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html

@ -16,7 +16,7 @@
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="numericFilterPredicateFormGroup">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="30" class="mat-block">
<mat-label></mat-label>
<mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
<mat-option *ngFor="let operation of numericOperations" [value]="operation">
@ -25,7 +25,7 @@
</mat-select>
</mat-form-field>
<tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
fxFlex="60"
fxFlex="70"
[valueType]="valueType"
formControlName="value">
</tb-filter-predicate-value>

4
ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html

@ -16,7 +16,7 @@
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="stringFilterPredicateFormGroup">
<div fxFlex="40" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div fxFlex="30" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
<mat-label></mat-label>
<mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
@ -29,7 +29,7 @@
</mat-checkbox>
</div>
<tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
fxFlex="60"
fxFlex="70"
[valueType]="valueTypeEnum.STRING"
formControlName="value">
</tb-filter-predicate-value>

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts

@ -27,7 +27,7 @@ export const DEFAULT_ID_SERVER = 123;
export const DEFAULT_ID_BOOTSTRAP = 111;
export const DEFAULT_HOST_NAME = 'localhost';
export const DEFAULT_PORT_SERVER_NO_SEC = 5685;
export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5691;
export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5686;
export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1;
export const DEFAULT_LIFE_TIME = 300;
export const DEFAULT_MIN_PERIOD = 1;

4
ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts

@ -30,6 +30,7 @@ import { EntitiesTableComponent } from '@home/components/entity/entities-table.c
import { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component';
import { ActivatedRoute } from '@angular/router';
import { EntityTabsComponent } from '../../components/entity/entity-tabs.component';
import { DAY, historyInterval } from '@shared/models/time/time.models';
export type EntityBooleanFunction<T extends BaseData<HasId>> = (entity: T) => boolean;
export type EntityStringFunction<T extends BaseData<HasId>> = (entity: T) => string;
@ -135,6 +136,7 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
onLoadAction: (route: ActivatedRoute) => void = null;
table: EntitiesTableComponent = null;
useTimePageLink = false;
defaultTimewindowInterval = historyInterval(DAY);
entityType: EntityType = null;
tableTitle = '';
selectionEnabled = true;
@ -162,7 +164,7 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
dataSource: (dataLoadedFunction: (col?: number, row?: number) => void)
=> EntitiesDataSource<L> = (dataLoadedFunction: (col?: number, row?: number) => void) => {
return new EntitiesDataSource(this.entitiesFetchFunction, this.entitySelectionEnabled, dataLoadedFunction);
};
}
detailsReadonly: EntityBooleanFunction<T> = () => false;
entitySelectionEnabled: EntityBooleanFunction<L> = () => true;
deleteEnabled: EntityBooleanFunction<T | L> = () => true;

1
ui-ngx/src/app/shared/models/query/query.models.ts

@ -285,6 +285,7 @@ export const dynamicValueSourceTypeTranslationMap = new Map<DynamicValueSourceTy
export interface DynamicValue<T> {
sourceType: DynamicValueSourceType;
sourceAttribute: string;
inherit?: boolean;
}
export interface FilterPredicateValue<T> {

4
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1849,7 +1849,9 @@
"no-dynamic-value": "No dynamic value",
"source-attribute": "Source attribute",
"switch-to-dynamic-value": "Switch to dynamic value",
"switch-to-default-value": "Switch to default value"
"switch-to-default-value": "Switch to default value",
"inherit-owner": "Inherit from owner",
"source-attribute-not-set": "If source attribute isn't set"
},
"fullscreen": {
"expand": "Expand to fullscreen",

Loading…
Cancel
Save