Commit bc4684c907ecebf581e8c6b4dd9763c6fa0af1fa

Authored by nickAS21
1 parent 58e544c3

Lwm2m: backEnd: refactoring security

Showing 19 changed files with 1750 additions and 1702 deletions
@@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap; @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.eclipse.californium.scandium.config.DtlsConnectorConfig; 19 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 import org.eclipse.leshan.core.model.StaticModel; 20 import org.eclipse.leshan.core.model.StaticModel;
  21 +import org.eclipse.leshan.core.util.Hex;
21 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager; 22 import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
22 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; 23 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
23 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder; 24 import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
@@ -28,18 +29,40 @@ import org.springframework.context.annotation.Primary; @@ -28,18 +29,40 @@ import org.springframework.context.annotation.Primary;
28 import org.springframework.stereotype.Component; 29 import org.springframework.stereotype.Component;
29 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore; 30 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore;
30 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore; 31 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore;
31 -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MSetSecurityStoreBootstrap;  
32 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; 32 import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; 34 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
35 -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; 35 +
  36 +import java.math.BigInteger;
  37 +import java.security.AlgorithmParameters;
  38 +import java.security.GeneralSecurityException;
  39 +import java.security.KeyFactory;
  40 +import java.security.KeyStore;
  41 +import java.security.KeyStoreException;
  42 +import java.security.PrivateKey;
  43 +import java.security.PublicKey;
  44 +import java.security.cert.CertificateEncodingException;
  45 +import java.security.cert.X509Certificate;
  46 +import java.security.interfaces.ECPublicKey;
  47 +import java.security.spec.ECGenParameterSpec;
  48 +import java.security.spec.ECParameterSpec;
  49 +import java.security.spec.ECPoint;
  50 +import java.security.spec.ECPrivateKeySpec;
  51 +import java.security.spec.ECPublicKeySpec;
  52 +import java.security.spec.KeySpec;
  53 +import java.util.Arrays;
  54 +
  55 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
36 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; 56 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  57 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
37 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; 58 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
38 59
39 @Slf4j 60 @Slf4j
40 @Component 61 @Component
41 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true')") 62 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true')")
42 public class LwM2MTransportBootstrapServerConfiguration { 63 public class LwM2MTransportBootstrapServerConfiguration {
  64 + private PublicKey publicKey;
  65 + private PrivateKey privateKey;
43 66
44 @Autowired 67 @Autowired
45 private LwM2MTransportContextBootstrap contextBs; 68 private LwM2MTransportContextBootstrap contextBs;
@@ -90,7 +113,7 @@ public class LwM2MTransportBootstrapServerConfiguration { @@ -90,7 +113,7 @@ public class LwM2MTransportBootstrapServerConfiguration {
90 builder.setDtlsConfig(dtlsConfig); 113 builder.setDtlsConfig(dtlsConfig);
91 114
92 /** Create credentials */ 115 /** Create credentials */
93 - new LwM2MSetSecurityStoreBootstrap(builder, contextBs, contextS, dtlsMode); 116 + LwM2MSetSecurityStoreBootstrap(builder, dtlsMode);
94 117
95 BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore); 118 BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore);
96 builder.setSessionManager(sessionManager); 119 builder.setSessionManager(sessionManager);
@@ -98,4 +121,151 @@ public class LwM2MTransportBootstrapServerConfiguration { @@ -98,4 +121,151 @@ public class LwM2MTransportBootstrapServerConfiguration {
98 /** Create BootstrapServer */ 121 /** Create BootstrapServer */
99 return builder.build(); 122 return builder.build();
100 } 123 }
  124 +
  125 + public void LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MSecurityMode dtlsMode) {
  126 +
  127 + /** Set securityStore with new registrationStore */
  128 +
  129 + switch (dtlsMode) {
  130 + /** Use No_Sec only */
  131 + case NO_SEC:
  132 + setServerWithX509Cert(builder, NO_SEC.code);
  133 + break;
  134 + /** Use PSK/RPK */
  135 + case PSK:
  136 + case RPK:
  137 + setRPK(builder);
  138 + break;
  139 + case X509:
  140 + setServerWithX509Cert(builder, X509.code);
  141 + break;
  142 + /** Use X509_EST only */
  143 + case X509_EST:
  144 + // TODO support sentinel pool and make pool configurable
  145 + break;
  146 + /** Use ather X509, PSK, No_Sec ?? */
  147 + default:
  148 + break;
  149 + }
  150 + }
  151 +
  152 + private void setRPK(LeshanBootstrapServerBuilder builder) {
  153 + try {
  154 + /** Get Elliptic Curve Parameter spec for secp256r1 */
  155 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  156 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  157 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  158 + if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {
  159 + /** Get point values */
  160 + byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());
  161 + byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());
  162 + /** Create key specs */
  163 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  164 + parameterSpec);
  165 + /** Get keys */
  166 + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  167 + }
  168 + if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) {
  169 + /** Get point values */
  170 + byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray());
  171 + /** Create key specs */
  172 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  173 + /** Get keys */
  174 + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  175 + }
  176 + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
  177 + this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  178 + builder.setPublicKey(this.publicKey);
  179 + builder.setPrivateKey(this.privateKey);
  180 + this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey);
  181 + getParamsRPK();
  182 + }
  183 + } catch (GeneralSecurityException | IllegalArgumentException e) {
  184 + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
  185 + throw new RuntimeException(e);
  186 + }
  187 + }
  188 +
  189 + private void setServerWithX509Cert(LeshanBootstrapServerBuilder builder, int securityModeCode) {
  190 + try {
  191 + if (this.contextS.getCtxServer().getKeyStoreValue() != null) {
  192 + KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue();
  193 + setBuilderX509(builder);
  194 + X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias());
  195 + if (rootCAX509Cert != null && securityModeCode == X509.code) {
  196 + X509Certificate[] trustedCertificates = new X509Certificate[1];
  197 + trustedCertificates[0] = rootCAX509Cert;
  198 + builder.setTrustedCertificates(trustedCertificates);
  199 + } else {
  200 + /** by default trust all */
  201 + builder.setTrustedCertificates(new X509Certificate[0]);
  202 + }
  203 + }
  204 + else {
  205 + /** by default trust all */
  206 + builder.setTrustedCertificates(new X509Certificate[0]);
  207 + log.error("Unable to load X509 files for BootStrapServer");
  208 + }
  209 + } catch (KeyStoreException ex) {
  210 + log.error("[{}] Unable to load X509 files server", ex.getMessage());
  211 + }
  212 +
  213 + }
  214 +
  215 + private void setBuilderX509(LeshanBootstrapServerBuilder builder) {
  216 + /**
  217 + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
  218 + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
  219 + */
  220 + try {
  221 + X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());
  222 + this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());
  223 + if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  224 + builder.setPrivateKey(this.privateKey);
  225 + }
  226 + if (serverCertificate != null) {
  227 + builder.setCertificateChain(new X509Certificate[]{serverCertificate});
  228 + this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate);
  229 + infoParamsX509(serverCertificate);
  230 + }
  231 + } catch (Exception ex) {
  232 + log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
  233 + }
  234 + }
  235 +
  236 + private void getParamsRPK() {
  237 + if (this.publicKey instanceof ECPublicKey) {
  238 + /** Get x coordinate */
  239 + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
  240 + if (x[0] == 0)
  241 + x = Arrays.copyOfRange(x, 1, x.length);
  242 +
  243 + /** Get Y coordinate */
  244 + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
  245 + if (y[0] == 0)
  246 + y = Arrays.copyOfRange(y, 1, y.length);
  247 +
  248 + /** Get Curves params */
  249 + String params = ((ECPublicKey) this.publicKey).getParams().toString();
  250 + log.info(
  251 + " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
  252 + params, Hex.encodeHexString(x), Hex.encodeHexString(y),
  253 + Hex.encodeHexString(this.publicKey.getEncoded()),
  254 + Hex.encodeHexString(this.privateKey.getEncoded()));
  255 + } else {
  256 + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
  257 + }
  258 + }
  259 +
  260 + private void infoParamsX509(X509Certificate certificate) {
  261 + try {
  262 + log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
  263 + Hex.encodeHexString(certificate.getEncoded()),
  264 + Hex.encodeHexString(this.privateKey.getEncoded()));
  265 + } catch (CertificateEncodingException e) {
  266 + log.error("", e);
  267 + }
  268 + }
  269 +
  270 +
101 } 271 }
@@ -29,10 +29,10 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; @@ -29,10 +29,10 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore;
29 import org.eclipse.leshan.server.security.SecurityInfo; 29 import org.eclipse.leshan.server.security.SecurityInfo;
30 import org.springframework.beans.factory.annotation.Autowired; 30 import org.springframework.beans.factory.annotation.Autowired;
31 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 31 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
32 -import org.springframework.stereotype.Component; 32 +import org.springframework.stereotype.Service;
33 import org.thingsboard.server.gen.transport.TransportProtos; 33 import org.thingsboard.server.gen.transport.TransportProtos;
34 -import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo;  
35 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 34 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  35 +import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo;
36 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; 36 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
37 import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener; 37 import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener;
38 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer; 38 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
@@ -53,14 +53,14 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle @@ -53,14 +53,14 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
53 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getBootstrapParametersFromThingsboard; 53 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getBootstrapParametersFromThingsboard;
54 54
55 @Slf4j 55 @Slf4j
56 -@Component("LwM2MBootstrapSecurityStore") 56 +@Service("LwM2MBootstrapSecurityStore")
57 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' && '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true' && '${transport.lwm2m.bootstrap.enable}'=='true')") 57 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' && '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true' && '${transport.lwm2m.bootstrap.enable}'=='true')")
58 public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { 58 public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
59 59
60 private final EditableBootstrapConfigStore bootstrapConfigStore; 60 private final EditableBootstrapConfigStore bootstrapConfigStore;
61 61
62 @Autowired 62 @Autowired
63 - LwM2MGetSecurityInfo lwM2MGetSecurityInfo; 63 + LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo;
64 64
65 @Autowired 65 @Autowired
66 public LwM2MTransportContextServer context; 66 public LwM2MTransportContextServer context;
@@ -72,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -72,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
72 @Override 72 @Override
73 public List<SecurityInfo> getAllByEndpoint(String endPoint) { 73 public List<SecurityInfo> getAllByEndpoint(String endPoint) {
74 String endPointKey = endPoint; 74 String endPointKey = endPoint;
75 - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);  
76 - if (store.getBootstrapJsonCredential() != null) { 75 + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);
  76 + if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
77 /** add value to store from BootstrapJson */ 77 /** add value to store from BootstrapJson */
78 this.setBootstrapConfigScurityInfo(store); 78 this.setBootstrapConfigScurityInfo(store);
79 BootstrapConfig bsConfigNew = store.getBootstrapConfig(); 79 BootstrapConfig bsConfigNew = store.getBootstrapConfig();
@@ -86,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -86,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
86 } 86 }
87 bootstrapConfigStore.add(endPoint, bsConfigNew); 87 bootstrapConfigStore.add(endPoint, bsConfigNew);
88 } catch (InvalidConfigurationException e) { 88 } catch (InvalidConfigurationException e) {
89 - e.printStackTrace(); 89 + log.error("", e);
90 } 90 }
91 return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo()); 91 return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo());
92 } 92 }
@@ -96,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -96,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
96 96
97 @Override 97 @Override
98 public SecurityInfo getByIdentity(String identity) { 98 public SecurityInfo getByIdentity(String identity) {
99 - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.BOOTSTRAP);  
100 - /** add value to store from BootstrapJson */  
101 - this.setBootstrapConfigScurityInfo(store);  
102 -  
103 - if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { 99 + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.BOOTSTRAP);
  100 + if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  101 + /** add value to store from BootstrapJson */
  102 + this.setBootstrapConfigScurityInfo(store);
104 BootstrapConfig bsConfig = store.getBootstrapConfig(); 103 BootstrapConfig bsConfig = store.getBootstrapConfig();
105 if (bsConfig.security != null) { 104 if (bsConfig.security != null) {
106 try { 105 try {
107 bootstrapConfigStore.add(store.getEndPoint(), bsConfig); 106 bootstrapConfigStore.add(store.getEndPoint(), bsConfig);
108 } catch (InvalidConfigurationException e) { 107 } catch (InvalidConfigurationException e) {
109 - e.printStackTrace(); 108 + log.error("", e);
110 } 109 }
111 return store.getSecurityInfo(); 110 return store.getSecurityInfo();
112 } 111 }
@@ -154,33 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -154,33 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
154 private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) { 153 private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) {
155 try { 154 try {
156 JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential(); 155 JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential();
157 - ObjectMapper mapper = new ObjectMapper();  
158 - LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);  
159 - JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());  
160 - lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);  
161 - LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);  
162 - LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);  
163 - UUID sessionUUiD = UUID.randomUUID();  
164 - TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());  
165 - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo));  
166 - if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {  
167 - lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);  
168 - lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);  
169 - String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint());  
170 - context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);  
171 - return lwM2MBootstrapConfig;  
172 - }  
173 - else {  
174 - log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());  
175 - log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());  
176 - String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());  
177 - context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);  
178 - return null; 156 + if (bootstrapJsonCredential != null) {
  157 + ObjectMapper mapper = new ObjectMapper();
  158 + LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);
  159 + JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
  160 + lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);
  161 + LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
  162 + LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
  163 + UUID sessionUUiD = UUID.randomUUID();
  164 + TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
  165 + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo));
  166 + if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
  167 + lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
  168 + lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
  169 + String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint());
  170 + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
  171 + return lwM2MBootstrapConfig;
  172 + } else {
  173 + log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
  174 + log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
  175 + String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());
  176 + context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
  177 + return null;
  178 + }
179 } 179 }
180 } catch (JsonProcessingException e) { 180 } catch (JsonProcessingException e) {
181 log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage()); 181 log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage());
182 return null; 182 return null;
183 } 183 }
  184 + log.error("Unable to decode Json or Certificate for [{}]", store.getEndPoint());
  185 + return null;
184 } 186 }
185 187
186 /** 188 /**
@@ -193,7 +195,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -193,7 +195,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
193 * @return false if not sync between SecurityMode of Bootstrap credential and profile 195 * @return false if not sync between SecurityMode of Bootstrap credential and profile
194 */ 196 */
195 private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) { 197 private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
196 - return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) && 198 + return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
197 lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode())); 199 lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode()));
198 } 200 }
199 } 201 }
1 -/**  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.lwm2m.bootstrap.secure;  
17 -  
18 -import lombok.Data;  
19 -import lombok.extern.slf4j.Slf4j;  
20 -import org.eclipse.leshan.core.util.Hex;  
21 -import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;  
22 -import org.eclipse.leshan.server.security.EditableSecurityStore;  
23 -import org.thingsboard.server.transport.lwm2m.bootstrap.LwM2MTransportContextBootstrap;  
24 -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;  
25 -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;  
26 -  
27 -import java.math.BigInteger;  
28 -import java.security.AlgorithmParameters;  
29 -import java.security.KeyStore;  
30 -import java.security.PublicKey;  
31 -import java.security.PrivateKey;  
32 -import java.security.KeyFactory;  
33 -import java.security.GeneralSecurityException;  
34 -import java.security.KeyStoreException;  
35 -import java.security.cert.X509Certificate;  
36 -import java.security.interfaces.ECPublicKey;  
37 -import java.security.spec.ECGenParameterSpec;  
38 -import java.security.spec.ECParameterSpec;  
39 -import java.security.spec.ECPublicKeySpec;  
40 -import java.security.spec.ECPoint;  
41 -import java.security.spec.KeySpec;  
42 -import java.security.spec.ECPrivateKeySpec;  
43 -import java.util.Arrays;  
44 -  
45 -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;  
46 -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;  
47 -  
48 -@Slf4j  
49 -@Data  
50 -public class LwM2MSetSecurityStoreBootstrap {  
51 -  
52 - private KeyStore keyStore;  
53 - private PublicKey publicKey;  
54 - private PrivateKey privateKey;  
55 - private LwM2MTransportContextBootstrap contextBs;  
56 - private LwM2MTransportContextServer contextS;  
57 - private LeshanBootstrapServerBuilder builder;  
58 - EditableSecurityStore securityStore;  
59 -  
60 - public LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MTransportContextBootstrap contextBs, LwM2MTransportContextServer contextS, LwM2MSecurityMode dtlsMode) {  
61 - this.builder = builder;  
62 - this.contextBs = contextBs;  
63 - this.contextS = contextS;  
64 - /** Set securityStore with new registrationStore */  
65 -  
66 - switch (dtlsMode) {  
67 - /** Use No_Sec only */  
68 - case NO_SEC:  
69 - setServerWithX509Cert(NO_SEC.code);  
70 - break;  
71 - /** Use PSK/RPK */  
72 - case PSK:  
73 - case RPK:  
74 - setRPK();  
75 - break;  
76 - case X509:  
77 - setServerWithX509Cert(X509.code);  
78 - break;  
79 - /** Use X509_EST only */  
80 - case X509_EST:  
81 - // TODO support sentinel pool and make pool configurable  
82 - break;  
83 - /** Use ather X509, PSK, No_Sec ?? */  
84 - default:  
85 - break;  
86 - }  
87 - }  
88 -  
89 - private void setRPK() {  
90 - try {  
91 - /** Get Elliptic Curve Parameter spec for secp256r1 */  
92 - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");  
93 - algoParameters.init(new ECGenParameterSpec("secp256r1"));  
94 - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);  
95 - if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {  
96 - /** Get point values */  
97 - byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());  
98 - byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());  
99 - /** Create key specs */  
100 - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),  
101 - parameterSpec);  
102 - /** Get keys */  
103 - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);  
104 - }  
105 - if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) {  
106 - /** Get point values */  
107 - byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray());  
108 - /** Create key specs */  
109 - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);  
110 - /** Get keys */  
111 - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);  
112 - }  
113 - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&  
114 - this.privateKey != null && this.privateKey.getEncoded().length > 0) {  
115 - this.builder.setPublicKey(this.publicKey);  
116 - this.builder.setPrivateKey(this.privateKey);  
117 - this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey);  
118 - getParamsRPK();  
119 - }  
120 - } catch (GeneralSecurityException | IllegalArgumentException e) {  
121 - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());  
122 - throw new RuntimeException(e);  
123 - }  
124 - }  
125 -  
126 - private void setServerWithX509Cert(int securityModeCode) {  
127 - try {  
128 - if (this.contextS.getCtxServer().getKeyStoreValue() != null) {  
129 - KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue();  
130 - setBuilderX509();  
131 - X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias());  
132 - if (rootCAX509Cert != null && securityModeCode == X509.code) {  
133 - X509Certificate[] trustedCertificates = new X509Certificate[1];  
134 - trustedCertificates[0] = rootCAX509Cert;  
135 - this.builder.setTrustedCertificates(trustedCertificates);  
136 - } else {  
137 - /** by default trust all */  
138 - this.builder.setTrustedCertificates(new X509Certificate[0]);  
139 - }  
140 - }  
141 - else {  
142 - /** by default trust all */  
143 - this.builder.setTrustedCertificates(new X509Certificate[0]);  
144 - log.error("Unable to load X509 files for BootStrapServer");  
145 - }  
146 - } catch (KeyStoreException ex) {  
147 - log.error("[{}] Unable to load X509 files server", ex.getMessage());  
148 - }  
149 -  
150 - }  
151 -  
152 - private void setBuilderX509() {  
153 - /**  
154 - * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE  
155 - * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks  
156 - */  
157 - try {  
158 - X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());  
159 - this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());  
160 - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {  
161 - this.builder.setPrivateKey(this.privateKey);  
162 - }  
163 - if (serverCertificate != null) {  
164 - this.builder.setCertificateChain(new X509Certificate[]{serverCertificate});  
165 - this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate);  
166 - }  
167 - } catch (Exception ex) {  
168 - log.error("[{}] Unable to load KeyStore files server", ex.getMessage());  
169 - }  
170 - }  
171 -  
172 - private void getParamsRPK() {  
173 - if (this.publicKey instanceof ECPublicKey) {  
174 - /** Get x coordinate */  
175 - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();  
176 - if (x[0] == 0)  
177 - x = Arrays.copyOfRange(x, 1, x.length);  
178 -  
179 - /** Get Y coordinate */  
180 - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();  
181 - if (y[0] == 0)  
182 - y = Arrays.copyOfRange(y, 1, y.length);  
183 -  
184 - /** Get Curves params */  
185 - String params = ((ECPublicKey) this.publicKey).getParams().toString();  
186 - log.info(  
187 - " \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",  
188 - params, Hex.encodeHexString(x), Hex.encodeHexString(y),  
189 - Hex.encodeHexString(this.publicKey.getEncoded()),  
190 - Hex.encodeHexString(this.privateKey.getEncoded()));  
191 - } else {  
192 - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");  
193 - }  
194 - }  
195 -  
196 -// private void getParamsX509() {  
197 -// try {  
198 -// log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",  
199 -// Hex.encodeHexString(this.certificate.getEncoded()),  
200 -// Hex.encodeHexString(this.privateKey.getEncoded()));  
201 -// } catch (CertificateEncodingException e) {  
202 -// e.printStackTrace();  
203 -// }  
204 -// }  
205 -}  
@@ -73,15 +73,15 @@ public class LWM2MGenerationPSkRPkECC { @@ -73,15 +73,15 @@ public class LWM2MGenerationPSkRPkECC {
73 try { 73 try {
74 kpg = KeyPairGenerator.getInstance(algorithm, provider); 74 kpg = KeyPairGenerator.getInstance(algorithm, provider);
75 } catch (NoSuchAlgorithmException e) { 75 } catch (NoSuchAlgorithmException e) {
76 - e.printStackTrace(); 76 + log.error("", e);
77 } catch (NoSuchProviderException e) { 77 } catch (NoSuchProviderException e) {
78 - e.printStackTrace(); 78 + log.error("", e);
79 } 79 }
80 ECGenParameterSpec ecsp = new ECGenParameterSpec(nameParameterSpec); 80 ECGenParameterSpec ecsp = new ECGenParameterSpec(nameParameterSpec);
81 try { 81 try {
82 kpg.initialize(ecsp); 82 kpg.initialize(ecsp);
83 } catch (InvalidAlgorithmParameterException e) { 83 } catch (InvalidAlgorithmParameterException e) {
84 - e.printStackTrace(); 84 + log.error("", e);
85 } 85 }
86 86
87 KeyPair kp = kpg.genKeyPair(); 87 KeyPair kp = kpg.genKeyPair();
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java
@@ -44,11 +44,10 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PS @@ -44,11 +44,10 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PS
44 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; 44 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
45 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; 45 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
46 46
47 -  
48 @Slf4j 47 @Slf4j
49 @Component("LwM2MGetSecurityInfo") 48 @Component("LwM2MGetSecurityInfo")
50 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 49 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
51 -public class LwM2MGetSecurityInfo { 50 +public class LwM2mValidateCredentialsSecurityInfo {
52 51
53 @Autowired 52 @Autowired
54 public LwM2MTransportContextServer contextS; 53 public LwM2MTransportContextServer contextS;
@@ -57,7 +56,13 @@ public class LwM2MGetSecurityInfo { @@ -57,7 +56,13 @@ public class LwM2MGetSecurityInfo {
57 public LwM2MTransportContextBootstrap contextBS; 56 public LwM2MTransportContextBootstrap contextBS;
58 57
59 58
60 - public ReadResultSecurityStore getSecurityInfo(String endPoint, TypeServer keyValue) { 59 + /**
  60 + * Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials
  61 + * @param endPoint -
  62 + * @param keyValue -
  63 + * @return ValidateDeviceCredentialsResponseMsg and SecurityInfo
  64 + */
  65 + public ReadResultSecurityStore validateCredentialsSecurityInfo(String endPoint, TypeServer keyValue) {
61 CountDownLatch latch = new CountDownLatch(1); 66 CountDownLatch latch = new CountDownLatch(1);
62 final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1]; 67 final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1];
63 contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(), 68 contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(),
@@ -65,7 +70,7 @@ public class LwM2MGetSecurityInfo { @@ -65,7 +70,7 @@ public class LwM2MGetSecurityInfo {
65 @Override 70 @Override
66 public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { 71 public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
67 String credentialsBody = msg.getCredentialsBody(); 72 String credentialsBody = msg.getCredentialsBody();
68 - resultSecurityStore[0] = putSecurityInfo(endPoint, msg.getDeviceInfo().getDeviceName(), credentialsBody, keyValue); 73 + resultSecurityStore[0] = createSecurityInfo(endPoint, credentialsBody, keyValue);
69 resultSecurityStore[0].setMsg(msg); 74 resultSecurityStore[0].setMsg(msg);
70 Optional<DeviceProfile> deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray()); 75 Optional<DeviceProfile> deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray());
71 deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile)); 76 deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
@@ -74,20 +79,27 @@ public class LwM2MGetSecurityInfo { @@ -74,20 +79,27 @@ public class LwM2MGetSecurityInfo {
74 79
75 @Override 80 @Override
76 public void onError(Throwable e) { 81 public void onError(Throwable e) {
77 - log.trace("[{}] Failed to process credentials PSK: {}", endPoint, e);  
78 - resultSecurityStore[0] = putSecurityInfo(endPoint, null, null, null); 82 + log.trace("[{}] [{}] Failed to process credentials PSK ", endPoint, e.toString());
  83 + resultSecurityStore[0] = createSecurityInfo(endPoint, null, null);
79 latch.countDown(); 84 latch.countDown();
80 } 85 }
81 }); 86 });
82 try { 87 try {
83 latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS); 88 latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS);
84 } catch (InterruptedException e) { 89 } catch (InterruptedException e) {
85 - e.printStackTrace(); 90 + log.error("", e);
86 } 91 }
87 return resultSecurityStore[0]; 92 return resultSecurityStore[0];
88 } 93 }
89 94
90 - private ReadResultSecurityStore putSecurityInfo(String endPoint, String deviceName, String jsonStr, TypeServer keyValue) { 95 + /**
  96 + * Create new SecurityInfo
  97 + * @param endPoint -
  98 + * @param jsonStr -
  99 + * @param keyValue -
  100 + * @return SecurityInfo
  101 + */
  102 + private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, TypeServer keyValue) {
91 ReadResultSecurityStore result = new ReadResultSecurityStore(); 103 ReadResultSecurityStore result = new ReadResultSecurityStore();
92 JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr); 104 JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr);
93 if (objectMsg != null && !objectMsg.isJsonNull()) { 105 if (objectMsg != null && !objectMsg.isJsonNull()) {
@@ -103,20 +115,21 @@ public class LwM2MGetSecurityInfo { @@ -103,20 +115,21 @@ public class LwM2MGetSecurityInfo {
103 if (keyValue.equals(TypeServer.BOOTSTRAP)) { 115 if (keyValue.equals(TypeServer.BOOTSTRAP)) {
104 result.setBootstrapJsonCredential(object); 116 result.setBootstrapJsonCredential(object);
105 result.setEndPoint(endPoint); 117 result.setEndPoint(endPoint);
  118 + result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);
106 } else { 119 } else {
107 LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase()); 120 LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase());
108 switch (lwM2MSecurityMode) { 121 switch (lwM2MSecurityMode) {
109 case NO_SEC: 122 case NO_SEC:
110 - getClientSecurityInfoNoSec(result); 123 + createClientSecurityInfoNoSec(result);
111 break; 124 break;
112 case PSK: 125 case PSK:
113 - getClientSecurityInfoPSK(result, endPoint, object); 126 + createClientSecurityInfoPSK(result, endPoint, object);
114 break; 127 break;
115 case RPK: 128 case RPK:
116 - getClientSecurityInfoRPK(result, endPoint, object); 129 + createClientSecurityInfoRPK(result, endPoint, object);
117 break; 130 break;
118 case X509: 131 case X509:
119 - getClientSecurityInfoX509(result, endPoint); 132 + createClientSecurityInfoX509(result, endPoint);
120 break; 133 break;
121 default: 134 default:
122 break; 135 break;
@@ -127,12 +140,12 @@ public class LwM2MGetSecurityInfo { @@ -127,12 +140,12 @@ public class LwM2MGetSecurityInfo {
127 return result; 140 return result;
128 } 141 }
129 142
130 - private void getClientSecurityInfoNoSec(ReadResultSecurityStore result) { 143 + private void createClientSecurityInfoNoSec(ReadResultSecurityStore result) {
131 result.setSecurityInfo(null); 144 result.setSecurityInfo(null);
132 result.setSecurityMode(NO_SEC.code); 145 result.setSecurityMode(NO_SEC.code);
133 } 146 }
134 147
135 - private void getClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) { 148 + private void createClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) {
136 /** PSK Deserialization */ 149 /** PSK Deserialization */
137 String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null; 150 String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null;
138 if (identity != null && !identity.isEmpty()) { 151 if (identity != null && !identity.isEmpty()) {
@@ -152,7 +165,7 @@ public class LwM2MGetSecurityInfo { @@ -152,7 +165,7 @@ public class LwM2MGetSecurityInfo {
152 } 165 }
153 } 166 }
154 167
155 - private void getClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) { 168 + private void createClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) {
156 try { 169 try {
157 if (object.has("key") && object.get("key").isJsonPrimitive()) { 170 if (object.has("key") && object.get("key").isJsonPrimitive()) {
158 byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray()); 171 byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray());
@@ -167,7 +180,7 @@ public class LwM2MGetSecurityInfo { @@ -167,7 +180,7 @@ public class LwM2MGetSecurityInfo {
167 } 180 }
168 } 181 }
169 182
170 - private void getClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) { 183 + private void createClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) {
171 result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint)); 184 result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
172 result.setSecurityMode(X509.code); 185 result.setSecurityMode(X509.code);
173 } 186 }
@@ -33,10 +33,10 @@ import java.util.Optional; @@ -33,10 +33,10 @@ import java.util.Optional;
33 33
34 @Slf4j 34 @Slf4j
35 public class LwM2MSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { 35 public class LwM2MSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
36 - private LwM2MTransportService service; 36 + private LwM2MTransportServiceImpl service;
37 private TransportProtos.SessionInfoProto sessionInfo; 37 private TransportProtos.SessionInfoProto sessionInfo;
38 38
39 - public LwM2MSessionMsgListener(LwM2MTransportService service, TransportProtos.SessionInfoProto sessionInfo) { 39 + public LwM2MSessionMsgListener(LwM2MTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) {
40 this.service = service; 40 this.service = service;
41 this.sessionInfo = sessionInfo; 41 this.sessionInfo = sessionInfo;
42 } 42 }
@@ -31,20 +31,14 @@ import org.eclipse.leshan.core.node.LwM2mPath; @@ -31,20 +31,14 @@ import org.eclipse.leshan.core.node.LwM2mPath;
31 import org.eclipse.leshan.core.node.LwM2mSingleResource; 31 import org.eclipse.leshan.core.node.LwM2mSingleResource;
32 import org.eclipse.leshan.core.node.codec.CodecException; 32 import org.eclipse.leshan.core.node.codec.CodecException;
33 import org.eclipse.leshan.core.util.Hex; 33 import org.eclipse.leshan.core.util.Hex;
34 -import org.eclipse.leshan.server.californium.LeshanServer;  
35 import org.eclipse.leshan.server.californium.LeshanServerBuilder; 34 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
36 import org.nustaq.serialization.FSTConfiguration; 35 import org.nustaq.serialization.FSTConfiguration;
37 -import org.springframework.beans.factory.annotation.Autowired;  
38 -import org.springframework.beans.factory.annotation.Qualifier;  
39 -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;  
40 -import org.springframework.stereotype.Component;  
41 import org.thingsboard.server.common.data.DeviceProfile; 36 import org.thingsboard.server.common.data.DeviceProfile;
42 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; 37 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
43 import org.thingsboard.server.common.transport.TransportServiceCallback; 38 import org.thingsboard.server.common.transport.TransportServiceCallback;
44 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; 39 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
45 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; 40 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
46 41
47 -import javax.annotation.PostConstruct;  
48 import java.io.File; 42 import java.io.File;
49 import java.io.IOException; 43 import java.io.IOException;
50 import java.util.Arrays; 44 import java.util.Arrays;
@@ -53,8 +47,8 @@ import java.util.LinkedList; @@ -53,8 +47,8 @@ import java.util.LinkedList;
53 import java.util.Optional; 47 import java.util.Optional;
54 48
55 @Slf4j 49 @Slf4j
56 -@Component("LwM2MTransportHandler")  
57 -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 50 +//@Component("LwM2MTransportHandler")
  51 +//@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
58 public class LwM2MTransportHandler { 52 public class LwM2MTransportHandler {
59 53
60 // We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to 54 // We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to
@@ -114,32 +108,38 @@ public class LwM2MTransportHandler { @@ -114,32 +108,38 @@ public class LwM2MTransportHandler {
114 public static final String SERVICE_CHANNEL = "SERVICE"; 108 public static final String SERVICE_CHANNEL = "SERVICE";
115 public static final String RESPONSE_CHANNEL = "RESP"; 109 public static final String RESPONSE_CHANNEL = "RESP";
116 110
117 - @Autowired  
118 - @Qualifier("LeshanServerCert")  
119 - private LeshanServer lhServerCert; 111 +// @Autowired
  112 +// @Qualifier("LeshanServerCert")
  113 +// private LeshanServer lhServerCert;
  114 +//
  115 +// @Autowired
  116 +// @Qualifier("LeshanServerNoSecPskRpk")
  117 +// private LeshanServer lhServerNoSecPskRpk;
120 118
121 - @Autowired  
122 - @Qualifier("leshanServerNoSecPskRpk")  
123 - private LeshanServer lhServerNoSecPskRpk; 119 +// @Autowired
  120 +// @Qualifier("ServerListenerCert")
  121 +// private LwM2mServerListener serverListenerCert;
  122 +//
  123 +// @Autowired
  124 +// @Qualifier("ServerListenerNoSecPskRpk")
  125 +// private LwM2mServerListener serverListenerNoSecPskRpk;
124 126
125 - @Autowired  
126 - private LwM2MTransportService service;  
127 127
128 - @PostConstruct  
129 - public void init() {  
130 - try {  
131 - LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service);  
132 - this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener);  
133 - this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener);  
134 - this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener);  
135 - lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service);  
136 - this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener);  
137 - this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener);  
138 - this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener);  
139 - } catch (java.lang.NullPointerException e) {  
140 - log.error("init [{}]", e.toString());  
141 - }  
142 - } 128 +// @PostConstruct
  129 +// public void init() {
  130 +// try {
  131 +// serverListenerCert.init(lhServerCert);
  132 +// this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener);
  133 +// this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener);
  134 +// this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener);
  135 +// serverListenerNoSecPskRpk.init(lhServerNoSecPskRpk);
  136 +// this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener);
  137 +// this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener);
  138 +// this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener);
  139 +// } catch (Exception e) {
  140 +// log.error("init [{}]", e.toString());
  141 +// }
  142 +// }
143 143
144 public static NetworkConfig getCoapConfig() { 144 public static NetworkConfig getCoapConfig() {
145 NetworkConfig coapConfig; 145 NetworkConfig coapConfig;
@@ -217,7 +217,7 @@ public class LwM2MTransportHandler { @@ -217,7 +217,7 @@ public class LwM2MTransportHandler {
217 JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null; 217 JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null;
218 return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null; 218 return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null;
219 } catch (IOException e) { 219 } catch (IOException e) {
220 - e.printStackTrace(); 220 + log.error("", e);
221 } 221 }
222 } 222 }
223 return null; 223 return null;
@@ -232,7 +232,7 @@ public class LwM2MTransportHandler { @@ -232,7 +232,7 @@ public class LwM2MTransportHandler {
232 JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null; 232 JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null;
233 return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null; 233 return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null;
234 } catch (IOException e) { 234 } catch (IOException e) {
235 - e.printStackTrace(); 235 + log.error("", e);
236 } 236 }
237 } 237 }
238 return null; 238 return null;
@@ -86,7 +86,7 @@ public class LwM2MTransportRequest { @@ -86,7 +86,7 @@ public class LwM2MTransportRequest {
86 private LwM2mValueConverterImpl converter; 86 private LwM2mValueConverterImpl converter;
87 87
88 @Autowired 88 @Autowired
89 - LwM2MTransportService service; 89 + LwM2MTransportServiceImpl service;
90 90
91 91
92 @PostConstruct 92 @PostConstruct
@@ -20,10 +20,16 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig; @@ -20,10 +20,16 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; 20 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
21 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; 21 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
22 import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder; 22 import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
  23 +import org.eclipse.leshan.core.util.Hex;
23 import org.eclipse.leshan.server.californium.LeshanServer; 24 import org.eclipse.leshan.server.californium.LeshanServer;
24 import org.eclipse.leshan.server.californium.LeshanServerBuilder; 25 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
25 import org.eclipse.leshan.server.model.LwM2mModelProvider; 26 import org.eclipse.leshan.server.model.LwM2mModelProvider;
26 import org.eclipse.leshan.server.model.VersionedModelProvider; 27 import org.eclipse.leshan.server.model.VersionedModelProvider;
  28 +import org.eclipse.leshan.server.redis.RedisRegistrationStore;
  29 +import org.eclipse.leshan.server.redis.RedisSecurityStore;
  30 +import org.eclipse.leshan.server.security.DefaultAuthorizer;
  31 +import org.eclipse.leshan.server.security.EditableSecurityStore;
  32 +import org.eclipse.leshan.server.security.SecurityChecker;
27 import org.springframework.beans.factory.annotation.Autowired; 33 import org.springframework.beans.factory.annotation.Autowired;
28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 34 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
29 import org.springframework.context.annotation.Bean; 35 import org.springframework.context.annotation.Bean;
@@ -31,11 +37,33 @@ import org.springframework.context.annotation.ComponentScan; @@ -31,11 +37,33 @@ import org.springframework.context.annotation.ComponentScan;
31 import org.springframework.context.annotation.Configuration; 37 import org.springframework.context.annotation.Configuration;
32 import org.springframework.context.annotation.Primary; 38 import org.springframework.context.annotation.Primary;
33 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 39 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
34 -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2MSetSecurityStoreServer;  
35 import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore; 40 import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
36 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; 41 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
37 -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509; 42 +import redis.clients.jedis.Jedis;
  43 +import redis.clients.jedis.JedisPool;
  44 +import redis.clients.jedis.util.Pool;
  45 +
  46 +import java.math.BigInteger;
  47 +import java.net.URI;
  48 +import java.net.URISyntaxException;
  49 +import java.security.AlgorithmParameters;
  50 +import java.security.GeneralSecurityException;
  51 +import java.security.KeyFactory;
  52 +import java.security.KeyStoreException;
  53 +import java.security.PrivateKey;
  54 +import java.security.PublicKey;
  55 +import java.security.cert.X509Certificate;
  56 +import java.security.interfaces.ECPublicKey;
  57 +import java.security.spec.ECGenParameterSpec;
  58 +import java.security.spec.ECParameterSpec;
  59 +import java.security.spec.ECPoint;
  60 +import java.security.spec.ECPrivateKeySpec;
  61 +import java.security.spec.ECPublicKeySpec;
  62 +import java.security.spec.KeySpec;
  63 +import java.util.Arrays;
  64 +
38 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK; 65 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
  66 +import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
39 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig; 67 import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
40 68
41 69
@@ -45,6 +73,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle @@ -45,6 +73,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
45 @Configuration("LwM2MTransportServerConfiguration") 73 @Configuration("LwM2MTransportServerConfiguration")
46 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 74 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
47 public class LwM2MTransportServerConfiguration { 75 public class LwM2MTransportServerConfiguration {
  76 + private PublicKey publicKey;
  77 + private PrivateKey privateKey;
48 78
49 @Autowired 79 @Autowired
50 private LwM2MTransportContextServer context; 80 private LwM2MTransportContextServer context;
@@ -52,14 +82,26 @@ public class LwM2MTransportServerConfiguration { @@ -52,14 +82,26 @@ public class LwM2MTransportServerConfiguration {
52 @Autowired 82 @Autowired
53 private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; 83 private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
54 84
  85 + @Bean
  86 + public LwM2mServerListener lwM2mServerListenerCert() {
  87 + return new LwM2mServerListener();
  88 + }
  89 +
  90 + @Bean
  91 + public LwM2mServerListener lwM2mServerListenerNoSecPskRpk() {
  92 + return new LwM2mServerListener();
  93 + }
  94 +
55 @Primary 95 @Primary
56 @Bean(name = "LeshanServerCert") 96 @Bean(name = "LeshanServerCert")
57 public LeshanServer getLeshanServerCert() { 97 public LeshanServer getLeshanServerCert() {
58 log.info("Starting LwM2M transport ServerCert... PostConstruct"); 98 log.info("Starting LwM2M transport ServerCert... PostConstruct");
59 - return getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509); 99 + LeshanServer leshanServerCert = getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509);
  100 +
  101 + return leshanServerCert;
60 } 102 }
61 103
62 - @Bean(name = "leshanServerNoSecPskRpk") 104 + @Bean(name = "LeshanServerNoSecPskRpk")
63 public LeshanServer getLeshanServerNoSecPskRpk() { 105 public LeshanServer getLeshanServerNoSecPskRpk() {
64 log.info("Starting LwM2M transport ServerNoSecPskRpk... PostConstruct"); 106 log.info("Starting LwM2M transport ServerNoSecPskRpk... PostConstruct");
65 return getLeshanServer(this.context.getCtxServer().getServerPort(), this.context.getCtxServer().getServerSecurePort(), RPK); 107 return getLeshanServer(this.context.getCtxServer().getServerPort(), this.context.getCtxServer().getServerSecurePort(), RPK);
@@ -94,9 +136,178 @@ public class LwM2MTransportServerConfiguration { @@ -94,9 +136,178 @@ public class LwM2MTransportServerConfiguration {
94 /** Create DTLS security mode 136 /** Create DTLS security mode
95 * There can be only one DTLS security mode 137 * There can be only one DTLS security mode
96 */ 138 */
97 - new LwM2MSetSecurityStoreServer(builder, context, lwM2mInMemorySecurityStore, dtlsMode); 139 + this.LwM2MSetSecurityStoreServer(builder, dtlsMode);
98 140
99 /** Create LWM2M server */ 141 /** Create LWM2M server */
100 return builder.build(); 142 return builder.build();
101 } 143 }
  144 +
  145 + private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) {
  146 + /** Set securityStore with new registrationStore */
  147 + EditableSecurityStore securityStore = lwM2mInMemorySecurityStore;
  148 +
  149 + switch (dtlsMode) {
  150 + /** Use PSK only */
  151 + case PSK:
  152 + generatePSK_RPK();
  153 + if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  154 + builder.setPrivateKey(this.privateKey);
  155 + builder.setPublicKey(null);
  156 + infoParamsPSK();
  157 + }
  158 + break;
  159 + /** Use RPK only */
  160 + case RPK:
  161 + generatePSK_RPK();
  162 + if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
  163 + this.privateKey != null && this.privateKey.getEncoded().length > 0) {
  164 + builder.setPublicKey(this.publicKey);
  165 + builder.setPrivateKey(this.privateKey);
  166 + infoParamsRPK();
  167 + }
  168 + break;
  169 + /** Use x509 only */
  170 + case X509:
  171 + setServerWithX509Cert(builder);
  172 + break;
  173 + /** No security */
  174 + case NO_SEC:
  175 + builder.setTrustedCertificates(new X509Certificate[0]);
  176 + break;
  177 + /** Use x509 with EST */
  178 + case X509_EST:
  179 + // TODO support sentinel pool and make pool configurable
  180 + break;
  181 + case REDIS:
  182 + /**
  183 + * Set securityStore with new registrationStore (if use redis store)
  184 + * Connect to redis
  185 + */
  186 + Pool<Jedis> jedis = null;
  187 + try {
  188 + jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl()));
  189 + securityStore = new RedisSecurityStore(jedis);
  190 + builder.setRegistrationStore(new RedisRegistrationStore(jedis));
  191 + } catch (URISyntaxException e) {
  192 + log.error("", e);
  193 + }
  194 + break;
  195 + default:
  196 + }
  197 +
  198 + /** Set securityStore with registrationStore (if x509)*/
  199 + if (dtlsMode == X509) {
  200 + builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
  201 + @Override
  202 + protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
  203 + String expectedX509CommonName) {
  204 + return endpoint.startsWith(expectedX509CommonName);
  205 + }
  206 + }));
  207 + }
  208 +
  209 + /** Set securityStore with new registrationStore */
  210 + builder.setSecurityStore(securityStore);
  211 + }
  212 +
  213 + private void generatePSK_RPK() {
  214 + try {
  215 + /** Get Elliptic Curve Parameter spec for secp256r1 */
  216 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  217 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  218 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  219 + if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) {
  220 + /** Get point values */
  221 + byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray());
  222 + byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray());
  223 + /** Create key specs */
  224 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  225 + parameterSpec);
  226 + /** Get keys */
  227 + this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  228 + }
  229 + if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) {
  230 + /** Get point values */
  231 + byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray());
  232 + /** Create key specs */
  233 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  234 + /** Get keys */
  235 + this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  236 + }
  237 + } catch (GeneralSecurityException | IllegalArgumentException e) {
  238 + log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
  239 + throw new RuntimeException(e);
  240 + }
  241 + }
  242 +
  243 + private void infoParamsPSK() {
  244 + log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]",
  245 + Hex.encodeHexString(this.privateKey.getEncoded()),
  246 + this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort()));
  247 + }
  248 +
  249 + private void infoParamsRPK() {
  250 + if (this.publicKey instanceof ECPublicKey) {
  251 + /** Get x coordinate */
  252 + byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
  253 + if (x[0] == 0)
  254 + x = Arrays.copyOfRange(x, 1, x.length);
  255 +
  256 + /** Get Y coordinate */
  257 + byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
  258 + if (y[0] == 0)
  259 + y = Arrays.copyOfRange(y, 1, y.length);
  260 +
  261 + /** Get Curves params */
  262 + String params = ((ECPublicKey) this.publicKey).getParams().toString();
  263 + log.info(
  264 + " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
  265 + params, Hex.encodeHexString(x), Hex.encodeHexString(y),
  266 + Hex.encodeHexString(this.publicKey.getEncoded()),
  267 + Hex.encodeHexString(this.privateKey.getEncoded()));
  268 + } else {
  269 + throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
  270 + }
  271 + }
  272 +
  273 +
  274 + private void setServerWithX509Cert(LeshanServerBuilder builder) {
  275 + try {
  276 + if (this.context.getCtxServer().getKeyStoreValue() != null) {
  277 + setBuilderX509(builder);
  278 + X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias());
  279 + if (rootCAX509Cert != null) {
  280 + X509Certificate[] trustedCertificates = new X509Certificate[1];
  281 + trustedCertificates[0] = rootCAX509Cert;
  282 + builder.setTrustedCertificates(trustedCertificates);
  283 + } else {
  284 + /** by default trust all */
  285 + builder.setTrustedCertificates(new X509Certificate[0]);
  286 + }
  287 + } else {
  288 + /** by default trust all */
  289 + builder.setTrustedCertificates(new X509Certificate[0]);
  290 + log.error("Unable to load X509 files for LWM2MServer");
  291 + }
  292 + } catch (KeyStoreException ex) {
  293 + log.error("[{}] Unable to load X509 files server", ex.getMessage());
  294 + }
  295 + }
  296 +
  297 + private void setBuilderX509(LeshanServerBuilder builder) {
  298 + /**
  299 + * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
  300 + * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
  301 + */
  302 + try {
  303 + X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias());
  304 + PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());
  305 + builder.setPrivateKey(privateKey);
  306 + builder.setCertificateChain(new X509Certificate[]{serverCertificate});
  307 + } catch (Exception ex) {
  308 + log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
  309 + }
  310 + }
  311 +
  312 +
102 } 313 }
@@ -20,14 +20,15 @@ import org.eclipse.leshan.server.californium.LeshanServer; @@ -20,14 +20,15 @@ import org.eclipse.leshan.server.californium.LeshanServer;
20 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.beans.factory.annotation.Qualifier; 21 import org.springframework.beans.factory.annotation.Qualifier;
22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23 -import org.springframework.stereotype.Service; 23 +import org.springframework.stereotype.Component;
24 import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; 24 import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
25 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; 25 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  26 +
26 import javax.annotation.PostConstruct; 27 import javax.annotation.PostConstruct;
27 import javax.annotation.PreDestroy; 28 import javax.annotation.PreDestroy;
28 29
29 @Slf4j 30 @Slf4j
30 -@Service("LwM2MTransportServerInitializer") 31 +@Component("LwM2MTransportServerInitializer")
31 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 32 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
32 public class LwM2MTransportServerInitializer { 33 public class LwM2MTransportServerInitializer {
33 34
@@ -36,8 +37,18 @@ public class LwM2MTransportServerInitializer { @@ -36,8 +37,18 @@ public class LwM2MTransportServerInitializer {
36 private LeshanServer lhServerCert; 37 private LeshanServer lhServerCert;
37 38
38 @Autowired 39 @Autowired
39 - @Qualifier("leshanServerNoSecPskRpk") 40 + @Qualifier("LeshanServerNoSecPskRpk")
40 private LeshanServer lhServerNoSecPskRpk; 41 private LeshanServer lhServerNoSecPskRpk;
  42 +//
  43 +// @Autowired
  44 +// @Qualifier("LeshanServerListener")
  45 +// private LwM2mServerListener lwM2mServerListener;
  46 +
  47 + @Autowired
  48 + private LwM2mServerListener lwM2mServerListenerNoSecPskRpk;
  49 +
  50 + @Autowired
  51 + private LwM2mServerListener lwM2mServerListenerCert;
41 52
42 @Autowired 53 @Autowired
43 private LwM2MTransportContextServer context; 54 private LwM2MTransportContextServer context;
@@ -46,19 +57,33 @@ public class LwM2MTransportServerInitializer { @@ -46,19 +57,33 @@ public class LwM2MTransportServerInitializer {
46 public void init() { 57 public void init() {
47 if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC(); 58 if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC();
48 if (this.context.getCtxServer().isServerStartAll()) { 59 if (this.context.getCtxServer().isServerStartAll()) {
49 - this.lhServerCert.start();  
50 - this.lhServerNoSecPskRpk.start();  
51 - }  
52 - else { 60 + this.startLhServerCert();
  61 + this.startLhServerNoSecPskRpk();
  62 + } else {
53 if (this.context.getCtxServer().getServerDtlsMode() == LwM2MSecurityMode.X509.code) { 63 if (this.context.getCtxServer().getServerDtlsMode() == LwM2MSecurityMode.X509.code) {
54 - this.lhServerCert.start();  
55 - }  
56 - else {  
57 - this.lhServerNoSecPskRpk.start(); 64 + this.startLhServerCert();
  65 + } else {
  66 + this.startLhServerNoSecPskRpk();
58 } 67 }
59 } 68 }
60 } 69 }
61 70
  71 + private void startLhServerCert() {
  72 + this.lhServerCert.start();
  73 + LwM2mServerListener serverListenerCert = this.lwM2mServerListenerCert.init(this.lhServerCert);
  74 + this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener);
  75 + this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener);
  76 + this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener);
  77 + }
  78 +
  79 + private void startLhServerNoSecPskRpk() {
  80 + this.lhServerNoSecPskRpk.start();
  81 + LwM2mServerListener serverListenerNoSecPskRpk = this.lwM2mServerListenerNoSecPskRpk.init(this.lhServerNoSecPskRpk);
  82 + this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener);
  83 + this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener);
  84 + this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener);
  85 + }
  86 +
62 @PreDestroy 87 @PreDestroy
63 public void shutdown() { 88 public void shutdown() {
64 log.info("Stopping LwM2M transport Server!"); 89 log.info("Stopping LwM2M transport Server!");
@@ -15,1114 +15,42 @@ @@ -15,1114 +15,42 @@
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m.server; 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18 -import com.google.gson.Gson;  
19 -import com.google.gson.JsonArray;  
20 -import com.google.gson.JsonElement;  
21 -import com.google.gson.JsonObject;  
22 -import lombok.extern.slf4j.Slf4j;  
23 -import org.eclipse.leshan.core.model.ResourceModel;  
24 -import org.eclipse.leshan.core.node.LwM2mMultipleResource;  
25 -import org.eclipse.leshan.core.node.LwM2mObject;  
26 -import org.eclipse.leshan.core.node.LwM2mObjectInstance;  
27 -import org.eclipse.leshan.core.node.LwM2mPath;  
28 -import org.eclipse.leshan.core.node.LwM2mSingleResource;  
29 import org.eclipse.leshan.core.observation.Observation; 18 import org.eclipse.leshan.core.observation.Observation;
30 -import org.eclipse.leshan.core.request.ContentFormat;  
31 -import org.eclipse.leshan.core.request.WriteRequest;  
32 import org.eclipse.leshan.core.response.ReadResponse; 19 import org.eclipse.leshan.core.response.ReadResponse;
33 -import org.eclipse.leshan.core.util.NamedThreadFactory;  
34 import org.eclipse.leshan.server.californium.LeshanServer; 20 import org.eclipse.leshan.server.californium.LeshanServer;
35 import org.eclipse.leshan.server.registration.Registration; 21 import org.eclipse.leshan.server.registration.Registration;
36 -import org.springframework.beans.factory.annotation.Autowired;  
37 -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;  
38 -import org.springframework.stereotype.Service;  
39 import org.thingsboard.server.common.data.Device; 22 import org.thingsboard.server.common.data.Device;
40 import org.thingsboard.server.common.data.DeviceProfile; 23 import org.thingsboard.server.common.data.DeviceProfile;
41 -import org.thingsboard.server.common.transport.TransportService;  
42 -import org.thingsboard.server.common.transport.adaptor.AdaptorException;  
43 -import org.thingsboard.server.common.transport.adaptor.JsonConverter;  
44 -import org.thingsboard.server.common.transport.service.DefaultTransportService;  
45 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
46 -import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;  
47 -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;  
48 -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;  
49 -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;  
50 -import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;  
51 -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;  
52 -import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;  
53 -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;  
54 -import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;  
55 -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;  
56 25
57 -import javax.annotation.PostConstruct;  
58 -import java.util.ArrayList;  
59 -import java.util.Arrays;  
60 import java.util.Collection; 26 import java.util.Collection;
61 -import java.util.HashSet;  
62 -import java.util.LinkedHashSet;  
63 -import java.util.List;  
64 -import java.util.Map;  
65 import java.util.Optional; 27 import java.util.Optional;
66 -import java.util.Random;  
67 -import java.util.Set;  
68 -import java.util.UUID;  
69 -import java.util.concurrent.ConcurrentHashMap;  
70 -import java.util.concurrent.ConcurrentMap;  
71 -import java.util.concurrent.CountDownLatch;  
72 -import java.util.concurrent.ExecutorService;  
73 -import java.util.concurrent.Executors;  
74 -import java.util.concurrent.TimeUnit;  
75 -import java.util.concurrent.atomic.AtomicBoolean;  
76 -import java.util.stream.Collectors;  
77 28
78 -import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject;  
79 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED;  
80 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT;  
81 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST;  
82 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC;  
83 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC;  
84 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE;  
85 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ;  
86 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;  
87 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;  
88 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY;  
89 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE;  
90 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;  
91 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL;  
92 -import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback; 29 +public interface LwM2MTransportService {
93 30
94 -@Slf4j  
95 -@Service("LwM2MTransportService")  
96 -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")  
97 -public class LwM2MTransportService { 31 + void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations);
98 32
99 - private ExecutorService executorRegistered;  
100 - private ExecutorService executorUpdateRegistered;  
101 - private ExecutorService executorUnRegistered;  
102 - private LwM2mValueConverterImpl converter; 33 + void updatedReg(LeshanServer lwServer, Registration registration);
103 34
  35 + void unReg(Registration registration, Collection<Observation> observations);
104 36
105 - @Autowired  
106 - private TransportService transportService; 37 + void onSleepingDev(Registration registration);
107 38
108 - @Autowired  
109 - public LwM2MTransportContextServer context; 39 + void setCancelObservations(LeshanServer lwServer, Registration registration);
110 40
111 - @Autowired  
112 - private LwM2MTransportRequest lwM2MTransportRequest; 41 + void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path);
113 42
114 - @Autowired  
115 - LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore; 43 + void onObservationResponse(Registration registration, String path, ReadResponse response);
116 44
117 - @PostConstruct  
118 - public void init() {  
119 - this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);  
120 - this.executorRegistered = Executors.newCachedThreadPool(  
121 - new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));  
122 - this.executorUpdateRegistered = Executors.newCachedThreadPool(  
123 - new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));  
124 - this.executorUnRegistered = Executors.newCachedThreadPool(  
125 - new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));  
126 - this.converter = LwM2mValueConverterImpl.getInstance();  
127 - } 45 + void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
128 46
129 - /**  
130 - * Start registration device  
131 - * Create session: Map<String <registrationId >, LwM2MClient>  
132 - * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint)  
133 - * 1.1 When we initialize the registration, we register the session by endpoint.  
134 - * 1.2 If the server has incomplete requests (canceling the registration of the previous session),  
135 - * delete the previous session only by the previous registration.getId  
136 - * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId  
137 - * 1.2 Remove from sessions Model by enpPoint  
138 - * Next -> Create new LwM2MClient for current session -> setModelClient...  
139 - *  
140 - * @param lwServer - LeshanServer  
141 - * @param registration - Registration LwM2M Client  
142 - * @param previousObsersations - may be null  
143 - */  
144 - public void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations) {  
145 - executorRegistered.submit(() -> {  
146 - try {  
147 - log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());  
148 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration);  
149 - if (lwM2MClient != null) {  
150 - lwM2MClient.setLwM2MTransportService(this);  
151 - lwM2MClient.setSessionUuid(UUID.randomUUID());  
152 - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);  
153 - this.setLwM2MClient(lwServer, registration, lwM2MClient);  
154 - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);  
155 - if (sessionInfo != null) {  
156 - lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));  
157 - lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));  
158 - lwM2MClient.setDeviceName(sessionInfo.getDeviceName());  
159 - lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType());  
160 - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));  
161 - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);  
162 - transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);  
163 - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);  
164 - } else {  
165 - log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);  
166 - }  
167 - } else {  
168 - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);  
169 - }  
170 - } catch (Throwable t) {  
171 - log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);  
172 - }  
173 - });  
174 - } 47 + void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile);
175 48
176 - /**  
177 - * if sessionInfo removed from sessions, then new registerAsyncSession  
178 - * @param lwServer - LeshanServer  
179 - * @param registration - Registration LwM2M Client  
180 - */  
181 - public void updatedReg(LeshanServer lwServer, Registration registration) {  
182 - executorUpdateRegistered.submit(() -> {  
183 - try {  
184 - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);  
185 - if (sessionInfo != null) {  
186 - this.checkInactivity(sessionInfo);  
187 - log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());  
188 - } else {  
189 - log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);  
190 - }  
191 - } catch (Throwable t) {  
192 - log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);  
193 - }  
194 - });  
195 - } 49 + void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt);
196 50
  51 + void doTrigger(LeshanServer lwServer, Registration registration, String path);
197 52
198 - /**  
199 - * @param registration - Registration LwM2M Client  
200 - * @param observations - All paths observations before unReg  
201 - * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect  
202 - */  
203 - public void unReg(Registration registration, Collection<Observation> observations) {  
204 - executorUnRegistered.submit(() -> {  
205 - try {  
206 - this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);  
207 - this.closeClientSession(registration);  
208 - } catch (Throwable t) {  
209 - log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);  
210 - }  
211 - });  
212 - } 53 + void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
213 54
214 - private void closeClientSession(Registration registration) {  
215 - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);  
216 - if (sessionInfo != null) {  
217 - transportService.deregisterSession(sessionInfo);  
218 - this.doCloseSession(sessionInfo);  
219 - lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId());  
220 - if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) {  
221 - this.syncSessionsAndProfiles();  
222 - }  
223 - log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());  
224 - } else {  
225 - log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);  
226 - }  
227 - }  
228 55
229 - public void onSleepingDev(Registration registration) {  
230 - log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());  
231 - //TODO: associate endpointId with device information.  
232 - }  
233 -  
234 - /**  
235 - * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,  
236 - * * if you need to do long time processing use a dedicated thread pool.  
237 - *  
238 - * @param registration -  
239 - */  
240 - protected void onAwakeDev(Registration registration) {  
241 - log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());  
242 - //TODO: associate endpointId with device information.  
243 - }  
244 -  
245 - /**  
246 - * This method is used to sync with sessions  
247 - * Removes a profile if not used in sessions  
248 - */  
249 - private void syncSessionsAndProfiles() {  
250 - Map<UUID, AttrTelemetryObserveValue> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()  
251 - .stream()  
252 - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));  
253 - profilesClone.forEach((k, v) -> {  
254 - String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()  
255 - .stream()  
256 - .filter(e -> e.getValue().getProfileUuid().equals(k))  
257 - .findFirst()  
258 - .map(Map.Entry::getKey) // return the key of the matching entry if found  
259 - .orElse("");  
260 - if (registrationId.isEmpty()) {  
261 - lwM2mInMemorySecurityStore.getProfiles().remove(k);  
262 - }  
263 - });  
264 - }  
265 -  
266 - /**  
267 - * #0 Add new ObjectModel to context  
268 - * Create new LwM2MClient for current session -> setModelClient...  
269 - * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client  
270 - * to get the client model with current values  
271 - * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse  
272 - *  
273 - * @param lwServer - LeshanServer  
274 - * @param registration - Registration LwM2M Client  
275 - * @param lwM2MClient - object with All parameters off client  
276 - */  
277 - private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {  
278 - Arrays.stream(registration.getObjectLinks()).forEach(url -> {  
279 - LwM2mPath pathIds = new LwM2mPath(url.getUrl());  
280 - if (pathIds.isObjectInstance() && !pathIds.isResource()) {  
281 - lwM2MClient.getPendingRequests().add(url.getUrl());  
282 - }  
283 - });  
284 - // #2  
285 - Arrays.stream(registration.getObjectLinks()).forEach(url -> {  
286 - LwM2mPath pathIds = new LwM2mPath(url.getUrl());  
287 - if (pathIds.isObjectInstance() && !pathIds.isResource()) {  
288 - lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),  
289 - lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);  
290 - }  
291 - });  
292 - }  
293 -  
294 - /**  
295 - * @param registration - Registration LwM2M Client  
296 - * @return - sessionInfo after access connect client  
297 - */  
298 - private SessionInfoProto getValidateSessionInfo(Registration registration) {  
299 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);  
300 - return getNewSessionInfoProto(lwM2MClient);  
301 -  
302 - }  
303 -  
304 - /**  
305 - *  
306 - * @param registrationId -  
307 - * @return -  
308 - */  
309 - private SessionInfoProto getValidateSessionInfo(String registrationId) {  
310 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);  
311 - return getNewSessionInfoProto(lwM2MClient);  
312 - }  
313 -  
314 - private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {  
315 - if (lwM2MClient != null) {  
316 - ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();  
317 - if (msg == null || msg.getDeviceInfo() == null) {  
318 - log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);  
319 - this.closeClientSession(lwM2MClient.getRegistration());  
320 - return null;  
321 - } else {  
322 - return SessionInfoProto.newBuilder()  
323 - .setNodeId(this.context.getNodeId())  
324 - .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())  
325 - .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())  
326 - .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())  
327 - .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())  
328 - .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())  
329 - .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())  
330 - .setDeviceName(msg.getDeviceInfo().getDeviceName())  
331 - .setDeviceType(msg.getDeviceInfo().getDeviceType())  
332 - .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())  
333 - .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())  
334 - .build();  
335 - }  
336 - }  
337 - return null;  
338 - }  
339 -  
340 - /**  
341 - * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe  
342 - * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use  
343 - * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection  
344 - * #2 Start observe  
345 - *  
346 - * @param lwM2MClient - LwM2M Client  
347 - */  
348 -  
349 - public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) {  
350 - // #1  
351 - this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null);  
352 - // #2  
353 - this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration());  
354 -  
355 - }  
356 -  
357 - /**  
358 - * If there is a difference in values between the current resource values and the shared attribute values  
359 - * when the client connects to the server  
360 - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable  
361 - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard  
362 - * #2.2 #1 size == 0 => continue normal process  
363 - *  
364 - * @param lwM2MClient - LwM2M Client  
365 - */  
366 - public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {  
367 - SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());  
368 - if (sessionInfo != null) {  
369 - //#1.1 + #1.2  
370 - List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);  
371 - if (attrSharedNames.size() > 0) {  
372 - //#2.1  
373 - try {  
374 - TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);  
375 - lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId());  
376 - transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));  
377 - } catch (AdaptorException e) {  
378 - log.warn("Failed to decode get attributes request", e);  
379 - }  
380 - }  
381 - // #2.2  
382 - else {  
383 - lwM2MClient.onSuccessOrErrorDelayedRequests(null);  
384 - }  
385 - }  
386 - }  
387 -  
388 - /**  
389 - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values  
390 - * #1 Get path resource by result attributesResponse  
391 - * #1.1 If two names have equal path => last time attribute  
392 - * #2.1 if there is a difference in values between the current resource values and the shared attribute values  
393 - * => sent to client Request Update of value (new value from shared attribute)  
394 - * and LwM2MClient.delayedRequests.add(path)  
395 - * #2.1 if there is not a difference in values between the current resource values and the shared attribute values  
396 - *  
397 - * @param attributesResponse -  
398 - * @param sessionInfo -  
399 - */  
400 - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {  
401 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);  
402 - if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) {  
403 - attributesResponse.getSharedAttributeListList().forEach(attr -> {  
404 - String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());  
405 - // #1.1  
406 - if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {  
407 - lwM2MClient.getDelayedRequests().put(path, attr);  
408 - } else {  
409 - lwM2MClient.getDelayedRequests().put(path, attr);  
410 - }  
411 - });  
412 - // #2.1  
413 - lwM2MClient.getDelayedRequests().forEach((k, v) -> {  
414 - ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();  
415 - listV.add(v.getKv());  
416 - this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);  
417 - });  
418 - lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());  
419 - if (lwM2MClient.getDelayedRequests().size() == 0) {  
420 - lwM2MClient.onSuccessOrErrorDelayedRequests(null);  
421 - }  
422 - }  
423 - }  
424 -  
425 - private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {  
426 - if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {  
427 - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,  
428 - ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),  
429 - true);  
430 - }  
431 - }  
432 -  
433 - /**  
434 - * Get names and keyNames from profile shared!!!! attr resources IsWritable  
435 - * @param lwM2MClient -  
436 - * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable  
437 - */  
438 - private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {  
439 - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());  
440 - Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);  
441 - ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);  
442 -  
443 - ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()  
444 - .stream()  
445 - .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&  
446 - context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))  
447 - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));  
448 -  
449 - Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();  
450 - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));  
451 - return new ArrayList<>(namesIsWritable);  
452 - }  
453 -  
454 -  
455 - /**  
456 - * Sent Attribute and Telemetry to Thingsboard  
457 - * #1 - get AttrName/TelemetryName with value:  
458 - * #1.1 from Client  
459 - * #1.2 from LwM2MClient:  
460 - * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile  
461 - * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)  
462 - * #2 - set Attribute/Telemetry  
463 - *  
464 - * @param registration - Registration LwM2M Client  
465 - */  
466 - private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {  
467 - JsonObject attributes = new JsonObject();  
468 - JsonObject telemetries = new JsonObject();  
469 - if (start) {  
470 - // #1.1  
471 - JsonObject attributeClient = this.getAttributeClient(registration);  
472 - if (attributeClient != null) {  
473 - attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));  
474 - }  
475 - }  
476 - // #1.2  
477 - CountDownLatch cancelLatch = new CountDownLatch(1);  
478 - this.getParametersFromProfile(attributes, telemetries, registration, paths);  
479 - cancelLatch.countDown();  
480 - try {  
481 - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);  
482 - } catch (InterruptedException e) {  
483 - log.error("[{}] updateAttrTelemetry", e.toString());  
484 - }  
485 - if (attributes.getAsJsonObject().entrySet().size() > 0)  
486 - this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);  
487 - if (telemetries.getAsJsonObject().entrySet().size() > 0)  
488 - this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);  
489 - }  
490 -  
491 - /**  
492 - * get AttrName/TelemetryName with value from Client  
493 - *  
494 - * @param registration -  
495 - * @return - JsonObject, format: {name: value}}  
496 - */  
497 - private JsonObject getAttributeClient(Registration registration) {  
498 - if (registration.getAdditionalRegistrationAttributes().size() > 0) {  
499 - JsonObject resNameValues = new JsonObject();  
500 - registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);  
501 - return resNameValues;  
502 - }  
503 - return null;  
504 - }  
505 -  
506 - /**  
507 - * @param attributes - new JsonObject  
508 - * @param telemetry - new JsonObject  
509 - * @param registration - Registration LwM2M Client  
510 - * result: add to JsonObject those resources to which the user is subscribed and they have a value  
511 - * if path==null add All resources else only one  
512 - * (attributes/telemetry): new {name(Attr/Telemetry):value}  
513 - */  
514 - private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {  
515 - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());  
516 - attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {  
517 - LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());  
518 - if (pathIds.isResource()) {  
519 - if (path == null || path.contains(p.getAsString())) {  
520 - this.addParameters(p.getAsString().toString(), attributes, registration);  
521 - }  
522 - }  
523 - });  
524 - attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {  
525 - LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());  
526 - if (pathIds.isResource()) {  
527 - if (path == null || path.contains(p.getAsString())) {  
528 - this.addParameters(p.getAsString().toString(), telemetry, registration);  
529 - }  
530 - }  
531 - });  
532 - }  
533 -  
534 - /**  
535 - * @param parameters - JsonObject attributes/telemetry  
536 - * @param registration - Registration LwM2M Client  
537 - */  
538 - private void addParameters(String path, JsonObject parameters, Registration registration) {  
539 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());  
540 - JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();  
541 - String resName = String.valueOf(names.get(path));  
542 - if (resName != null && !resName.isEmpty()) {  
543 - try {  
544 - String resValue = this.getResourceValueToString(lwM2MClient, path);  
545 - if (resValue != null) {  
546 - parameters.addProperty(resName, resValue);  
547 - }  
548 - } catch (Exception e) {  
549 - log.error(e.getStackTrace().toString());  
550 - }  
551 - }  
552 - }  
553 -  
554 - /**  
555 - * Prepare Sent to Thigsboard callback - Attribute or Telemetry  
556 - *  
557 - * @param msg - JsonArray: [{name: value}]  
558 - * @param topicName - Api Attribute or Telemetry  
559 - * @param registration - Id of Registration LwM2M Client  
560 - */  
561 - public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {  
562 - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);  
563 - if (sessionInfo != null) {  
564 - context.sentParametersOnThingsboard(msg, topicName, sessionInfo);  
565 - } else {  
566 - log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);  
567 - }  
568 - }  
569 -  
570 - /**  
571 - * Start observe  
572 - * #1 - Analyze:  
573 - * #1.1 path in observe == (attribute or telemetry)  
574 - * #2 Analyze after sent request (response):  
575 - * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation  
576 - * #2.2 Next: ObservationListener.onResponse *  
577 - *  
578 - * @param lwServer - LeshanServer  
579 - * @param registration - Registration LwM2M Client  
580 - */  
581 - private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {  
582 - if (lwServer.getObservationService().getObservations(registration).size() > 0) {  
583 - this.setCancelObservations(lwServer, registration);  
584 - }  
585 - UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid();  
586 - AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid);  
587 - attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> {  
588 - // #1.1  
589 - String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ?  
590 - p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ?  
591 - p.getAsString().toString() : null;  
592 - if (target != null) {  
593 - // #2  
594 - if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) {  
595 - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,  
596 - null, null, null, null, this.context.getCtxServer().getTimeout(),  
597 - false);  
598 - }  
599 - }  
600 - });  
601 - }  
602 -  
603 - public void setCancelObservations(LeshanServer lwServer, Registration registration) {  
604 - if (registration != null) {  
605 - Set<Observation> observations = lwServer.getObservationService().getObservations(registration);  
606 - observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));  
607 - }  
608 - }  
609 -  
610 - /**  
611 - * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());  
612 - * At server side this will not remove the observation from the observation store, to do it you need to use  
613 - * {@code ObservationService#cancelObservation()}  
614 - */  
615 - public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {  
616 - CountDownLatch cancelLatch = new CountDownLatch(1);  
617 - lwServer.getObservationService().cancelObservations(registration, path);  
618 - cancelLatch.countDown();  
619 - try {  
620 - cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);  
621 - } catch (InterruptedException e) {  
622 - e.printStackTrace();  
623 - }  
624 - }  
625 -  
626 - /**  
627 - * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile  
628 - * @param path - recourse from postObserveProfile  
629 - * @return rez - true if path observe is in attribute/telemetry  
630 - */  
631 - private boolean getValidateObserve(JsonElement parameters, String path) {  
632 - AtomicBoolean rez = new AtomicBoolean(false);  
633 - if (parameters.isJsonArray()) {  
634 - parameters.getAsJsonArray().forEach(p -> {  
635 - if (p.getAsString().toString().equals(path)) rez.set(true);  
636 - }  
637 - );  
638 - } else if (parameters.isJsonObject()) {  
639 - rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())  
640 - .filter(path::equals).findAny().orElse(null) != null);  
641 - }  
642 - return rez.get();  
643 - }  
644 -  
645 - /**  
646 - * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource  
647 - *  
648 - * @param registration - Registration LwM2M Client  
649 - * @param path - observe  
650 - * @param response - observe  
651 - */  
652 -  
653 - public void onObservationResponse(Registration registration, String path, ReadResponse response) {  
654 - if (response.getContent() != null) {  
655 - if (response.getContent() instanceof LwM2mObject) {  
656 -// LwM2mObject content = (LwM2mObject) response.getContent();  
657 - } else if (response.getContent() instanceof LwM2mObjectInstance) {  
658 -// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent();  
659 - } else if (response.getContent() instanceof LwM2mSingleResource) {  
660 - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();  
661 - this.onObservationSetResourcesValue(registration, content.getValue(), null, path);  
662 - } else if (response.getContent() instanceof LwM2mMultipleResource) {  
663 - LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();  
664 - this.onObservationSetResourcesValue(registration, null, content.getValues(), path);  
665 - }  
666 - }  
667 - }  
668 -  
669 - /**  
670 - * Sending observe value of resources to thingsboard  
671 - * #1 Return old Value Resource from LwM2MClient  
672 - * #2 Update new Resources (replace old Resource Value on new Resource Value)  
673 - *  
674 - * @param registration - Registration LwM2M Client  
675 - * @param value - LwM2mSingleResource response.getContent()  
676 - * @param values - LwM2mSingleResource response.getContent()  
677 - * @param path - resource  
678 - */  
679 - private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {  
680 - CountDownLatch respLatch = new CountDownLatch(1);  
681 - boolean isChange = false;  
682 - try {  
683 - // #1  
684 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);  
685 - LwM2mPath pathIds = new LwM2mPath(path);  
686 - log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values);  
687 - ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds);  
688 - ResourceValue resValueOld = lwM2MClient.getResources().get(path);  
689 - // #2  
690 - if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) {  
691 - ResourceValue resourceValue = new ResourceValue(values, null, true);  
692 - lwM2MClient.getResources().put(path, resourceValue);  
693 - isChange = true;  
694 - } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) {  
695 - ResourceValue resourceValue = new ResourceValue(null, value, false);  
696 - lwM2MClient.getResources().put(path, resourceValue);  
697 - isChange = true;  
698 - }  
699 - } finally {  
700 - respLatch.countDown();  
701 - }  
702 - try {  
703 - respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);  
704 - } catch (InterruptedException ex) {  
705 - ex.printStackTrace();  
706 - log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value);  
707 - }  
708 - if (isChange) {  
709 - Set<String> paths = new HashSet<>();  
710 - paths.add(path);  
711 - this.updateAttrTelemetry(registration, false, paths);  
712 - }  
713 - }  
714 -  
715 - /**  
716 - * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)  
717 - * config attr/telemetry... in profile  
718 - */  
719 - public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {  
720 - log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());  
721 - }  
722 -  
723 - /**  
724 - * Update - sent request in change value resources in Client  
725 - * Path to resources from profile equal keyName or from ModelObject equal name  
726 - * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)  
727 - * Delete - nothing *  
728 - *  
729 - * @param msg -  
730 - */  
731 - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {  
732 - if (msg.getSharedUpdatedCount() > 0) {  
733 - JsonElement el = JsonConverter.toJson(msg);  
734 - el.getAsJsonObject().entrySet().forEach(de -> {  
735 - String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());  
736 - String value = de.getValue().getAsString();  
737 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();  
738 - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));  
739 - ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));  
740 - if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {  
741 - if (resourceModel != null && resourceModel.operations.isWritable()) {  
742 - lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,  
743 - ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),  
744 - false);  
745 - } else {  
746 - log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);  
747 - String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);  
748 - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());  
749 - }  
750 - } else {  
751 - log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);  
752 - String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value);  
753 - this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());  
754 - }  
755 - });  
756 - } else if (msg.getSharedDeletedCount() > 0) {  
757 - log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);  
758 - }  
759 - }  
760 -  
761 - /**  
762 - * Get path to resource from profile equal keyName or from ModelObject equal name  
763 - * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)  
764 - *  
765 - * @param sessionInfo -  
766 - * @param name -  
767 - * @return path if path isPresent in postProfile  
768 - */  
769 - private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {  
770 - String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);  
771 -// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name);  
772 - return !profilePath.isEmpty() ? profilePath : null;  
773 - }  
774 -  
775 - /**  
776 - * @param profile -  
777 - * @param path -  
778 - * @return true if path isPresent in postAttributeProfile  
779 - */  
780 - private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) {  
781 - Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);  
782 - return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();  
783 - }  
784 -  
785 - /**  
786 - * @param profile -  
787 - * @param path -  
788 - * @return true if path isPresent in postAttributeProfile  
789 - */  
790 - private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) {  
791 - Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class);  
792 - return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();  
793 - }  
794 -  
795 -  
796 - /**  
797 - * Get path to resource from profile equal keyName  
798 - *  
799 - * @param sessionInfo -  
800 - * @param name -  
801 - * @return -  
802 - */  
803 - private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {  
804 - AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));  
805 - return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()  
806 - .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)  
807 - .orElse("");  
808 - }  
809 -  
810 - /**  
811 - * Update resource (attribute) value on thingsboard after update value in client  
812 - *  
813 - * @param registration -  
814 - * @param path -  
815 - * @param request -  
816 - */  
817 - public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {  
818 - ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path));  
819 - if (resource.multiple) {  
820 - this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);  
821 - } else {  
822 - this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);  
823 - }  
824 - if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null)  
825 - .onSuccessOrErrorDelayedRequests(request.getPath().toString());  
826 - }  
827 -  
828 - /**  
829 - * @param sessionInfo -  
830 - * @param deviceProfile -  
831 - */  
832 - public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {  
833 - Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()  
834 - .stream()  
835 - .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))  
836 - .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));  
837 - if (registrationIds.size() > 0) {  
838 - this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);  
839 - }  
840 - }  
841 -  
842 - /**  
843 - * @param sessionInfo -  
844 - * @param device -  
845 - * @param deviceProfileOpt -  
846 - */  
847 - public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {  
848 - Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()  
849 - .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))  
850 - .map(Map.Entry::getKey)  
851 - .findFirst();  
852 - registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));  
853 - }  
854 -  
855 - /**  
856 - * Update parameters device in LwM2MClient  
857 - * If new deviceProfile != old deviceProfile => update deviceProfile  
858 - *  
859 - * @param registrationId -  
860 - * @param device -  
861 - */  
862 - private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {  
863 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);  
864 - lwM2MClient.setDeviceName(device.getName());  
865 - if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {  
866 - Set<String> registrationIds = new HashSet<>();  
867 - registrationIds.add(registrationId);  
868 - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));  
869 - }  
870 -  
871 - lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());  
872 - }  
873 -  
874 - /**  
875 - * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName)  
876 - * #2 Update in lwM2MClient: ...Profile if changes from update device  
877 - * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName)  
878 - * #3.1 Attribute isChange (add&del)  
879 - * #3.2 Telemetry isChange (add&del)  
880 - * #3.3 KeyName isChange (add)  
881 - * #4 update  
882 - * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard  
883 - * #4.2 del  
884 - * -- if add attributes includes del telemetry - result del for observe  
885 - * #5  
886 - * #5.1 Observe isChange (add&del)  
887 - * #5.2 Observe.add  
888 - * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client  
889 - * #5.3 Observe.del  
890 - * -- different between newObserve and oldObserve: sent Request cancel observe to client  
891 - *  
892 - * @param registrationIds -  
893 - * @param deviceProfile -  
894 - */  
895 - public void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) {  
896 -  
897 - AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());  
898 - if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) {  
899 -  
900 - // #1  
901 - JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile();  
902 - Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class);  
903 - JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile();  
904 - Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class);  
905 - JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile();  
906 - JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile();  
907 -  
908 - AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());  
909 - JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile();  
910 - Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class);  
911 - JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile();  
912 - Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class);  
913 - JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile();  
914 - JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile();  
915 -  
916 - // #3  
917 - ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters();  
918 - // #3.1  
919 - if (!attributeOld.equals(attributeNew)) {  
920 - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew);  
921 - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());  
922 - sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());  
923 - }  
924 - // #3.2  
925 - if (!attributeOld.equals(attributeNew)) {  
926 - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew);  
927 - sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());  
928 - sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());  
929 - }  
930 - // #3.3  
931 - if (!keyNameOld.equals(keyNameNew)) {  
932 - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class),  
933 - new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class));  
934 - sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());  
935 - }  
936 -  
937 - // #4.1 add  
938 - if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) {  
939 - // update value in Resources  
940 - registrationIds.forEach(registrationId -> {  
941 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);  
942 - LeshanServer lwServer = lwM2MClient.getLwServer();  
943 - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);  
944 - log.warn("[{}] # 4.1", registration.getEndpoint());  
945 - this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);  
946 - // sent attr/telemetry to tingsboard for new path  
947 - this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd());  
948 - });  
949 - }  
950 - // #4.2 del  
951 - if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) {  
952 - ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel());  
953 - sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel());  
954 - }  
955 -  
956 - // #5.1  
957 - if (!observeOld.equals(observeNew)) {  
958 - Set observeSetOld = new Gson().fromJson(observeOld, Set.class);  
959 - Set observeSetNew = new Gson().fromJson(observeNew, Set.class);  
960 - //#5.2 add  
961 - // path Attr/Telemetry includes newObserve  
962 - attributeSetOld.addAll(telemetrySetOld);  
963 - ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe  
964 - attributeSetNew.addAll(telemetrySetNew);  
965 - ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe  
966 - // does not include oldObserve  
967 - ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd());  
968 - // sent Request observe to Client  
969 - registrationIds.forEach(registrationId -> {  
970 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId);  
971 - LeshanServer lwServer = lwM2MClient.getLwServer();  
972 - Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);  
973 - log.warn("[{}] # 5.1", registration.getEndpoint());  
974 - this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);  
975 - // 5.3 del  
976 - // sent Request cancel observe to Client  
977 - this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel());  
978 - });  
979 - }  
980 - }  
981 - }  
982 -  
983 - /**  
984 - * Compare old list with new list after change AttrTelemetryObserve in config Profile  
985 - *  
986 - * @param parametersOld -  
987 - * @param parametersNew -  
988 - * @return ResultsAnalyzerParameters: add && new  
989 - */  
990 - private ResultsAnalyzerParameters getAnalyzerParameters(Set<String> parametersOld, Set<String> parametersNew) {  
991 - ResultsAnalyzerParameters analyzerParameters = null;  
992 - if (!parametersOld.equals(parametersNew)) {  
993 - analyzerParameters = new ResultsAnalyzerParameters();  
994 - analyzerParameters.setPathPostParametersAdd(parametersNew  
995 - .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet()));  
996 - analyzerParameters.setPathPostParametersDel(parametersOld  
997 - .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet()));  
998 - }  
999 - return analyzerParameters;  
1000 - }  
1001 -  
1002 - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {  
1003 - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();  
1004 - Set<String> paths = keyNameNew.entrySet()  
1005 - .stream()  
1006 - .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))  
1007 - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();  
1008 - analyzerParameters.setPathPostParametersAdd(paths);  
1009 - return analyzerParameters;  
1010 - }  
1011 -  
1012 - private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {  
1013 - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();  
1014 - analyzerParameters.setPathPostParametersAdd(parametersObserve  
1015 - .stream().filter(parameters::contains).collect(Collectors.toSet()));  
1016 - return analyzerParameters;  
1017 - }  
1018 -  
1019 - /**  
1020 - * Update Resource value after change RezAttrTelemetry in config Profile  
1021 - * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()  
1022 - *  
1023 - * @param lwServer - LeshanServer  
1024 - * @param registration - Registration LwM2M Client  
1025 - * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]  
1026 - */  
1027 - private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) {  
1028 - targets.forEach(target -> {  
1029 - LwM2mPath pathIds = new LwM2mPath(target);  
1030 - if (pathIds.isResource()) {  
1031 - if (GET_TYPE_OPER_READ.equals(typeOper)) {  
1032 - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,  
1033 - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),  
1034 - false);  
1035 - } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {  
1036 - lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,  
1037 - null, null, null, null, this.context.getCtxServer().getTimeout(),  
1038 - false);  
1039 - }  
1040 - }  
1041 - });  
1042 - }  
1043 -  
1044 - private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {  
1045 - LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);  
1046 - paramAnallyzer.forEach(p -> {  
1047 - if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) {  
1048 - this.setCancelObservationRecourse(lwServer, registration, p);  
1049 - }  
1050 - }  
1051 - );  
1052 - }  
1053 -  
1054 - private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) {  
1055 - ResourceValue resourceValue = null;  
1056 - if (pathIds.isResource()) {  
1057 - resourceValue = lwM2MClient.getResources().get(pathIds.toString());  
1058 - }  
1059 - return resourceValue;  
1060 - }  
1061 -  
1062 - /**  
1063 - * Trigger Server path = "/1/0/8"  
1064 - *  
1065 - * Trigger bootStrap path = "/1/0/9" - have to implemented on client  
1066 - */  
1067 - public void doTrigger(LeshanServer lwServer, Registration registration, String path) {  
1068 - lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,  
1069 - ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),  
1070 - false);  
1071 - }  
1072 -  
1073 - /**  
1074 - * Session device in thingsboard is closed  
1075 - *  
1076 - * @param sessionInfo - lwm2m client  
1077 - */  
1078 - private void doCloseSession(SessionInfoProto sessionInfo) {  
1079 - TransportProtos.SessionEvent event = SessionEvent.CLOSED;  
1080 - TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()  
1081 - .setSessionType(TransportProtos.SessionType.ASYNC)  
1082 - .setEvent(event).build();  
1083 - transportService.process(sessionInfo, msg, null);  
1084 - }  
1085 -  
1086 - /**  
1087 - * Deregister session in transport  
1088 - *  
1089 - * @param sessionInfo - lwm2m client  
1090 - */  
1091 - private void doDisconnect(SessionInfoProto sessionInfo) {  
1092 - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);  
1093 - transportService.deregisterSession(sessionInfo);  
1094 - }  
1095 -  
1096 - private void checkInactivityAndReportActivity() {  
1097 - lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));  
1098 - }  
1099 -  
1100 - /**  
1101 - * if sessionInfo removed from sessions, then new registerAsyncSession  
1102 - * @param sessionInfo -  
1103 - */  
1104 - private void checkInactivity(SessionInfoProto sessionInfo) {  
1105 - if (transportService.reportActivity(sessionInfo) == null) {  
1106 - transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));  
1107 - }  
1108 - }  
1109 -  
1110 - public void sentLogsToThingsboard(String msg, Registration registration) {  
1111 - if (msg != null) {  
1112 - JsonObject telemetries = new JsonObject();  
1113 - telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);  
1114 - this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration);  
1115 - }  
1116 - }  
1117 -  
1118 - /**  
1119 - * @param path - path resource  
1120 - * @return - value of Resource or null  
1121 - */  
1122 - public String getResourceValueToString(LwM2MClient lwM2MClient, String path) {  
1123 - LwM2mPath pathIds = new LwM2mPath(path);  
1124 - ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds);  
1125 - return (resourceValue == null) ? null :  
1126 - (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);  
1127 - }  
1128 } 56 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server;
  17 +
  18 +import com.google.gson.Gson;
  19 +import com.google.gson.JsonArray;
  20 +import com.google.gson.JsonElement;
  21 +import com.google.gson.JsonObject;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.eclipse.leshan.core.model.ResourceModel;
  24 +import org.eclipse.leshan.core.node.LwM2mMultipleResource;
  25 +import org.eclipse.leshan.core.node.LwM2mObject;
  26 +import org.eclipse.leshan.core.node.LwM2mObjectInstance;
  27 +import org.eclipse.leshan.core.node.LwM2mPath;
  28 +import org.eclipse.leshan.core.node.LwM2mSingleResource;
  29 +import org.eclipse.leshan.core.observation.Observation;
  30 +import org.eclipse.leshan.core.request.ContentFormat;
  31 +import org.eclipse.leshan.core.request.WriteRequest;
  32 +import org.eclipse.leshan.core.response.ReadResponse;
  33 +import org.eclipse.leshan.core.util.NamedThreadFactory;
  34 +import org.eclipse.leshan.server.californium.LeshanServer;
  35 +import org.eclipse.leshan.server.registration.Registration;
  36 +import org.springframework.beans.factory.annotation.Autowired;
  37 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  38 +import org.springframework.stereotype.Service;
  39 +import org.thingsboard.server.common.data.Device;
  40 +import org.thingsboard.server.common.data.DeviceProfile;
  41 +import org.thingsboard.server.common.transport.TransportService;
  42 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  43 +import org.thingsboard.server.common.transport.adaptor.JsonConverter;
  44 +import org.thingsboard.server.common.transport.service.DefaultTransportService;
  45 +import org.thingsboard.server.gen.transport.TransportProtos;
  46 +import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
  47 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  48 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
  49 +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  50 +import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
  51 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
  52 +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
  53 +import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
  54 +import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
  55 +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
  56 +
  57 +import javax.annotation.PostConstruct;
  58 +import java.util.ArrayList;
  59 +import java.util.Arrays;
  60 +import java.util.Collection;
  61 +import java.util.HashSet;
  62 +import java.util.LinkedHashSet;
  63 +import java.util.List;
  64 +import java.util.Map;
  65 +import java.util.Optional;
  66 +import java.util.Random;
  67 +import java.util.Set;
  68 +import java.util.UUID;
  69 +import java.util.concurrent.ConcurrentHashMap;
  70 +import java.util.concurrent.ConcurrentMap;
  71 +import java.util.concurrent.CountDownLatch;
  72 +import java.util.concurrent.ExecutorService;
  73 +import java.util.concurrent.Executors;
  74 +import java.util.concurrent.TimeUnit;
  75 +import java.util.concurrent.atomic.AtomicBoolean;
  76 +import java.util.concurrent.locks.Lock;
  77 +import java.util.concurrent.locks.ReadWriteLock;
  78 +import java.util.concurrent.locks.ReentrantReadWriteLock;
  79 +import java.util.stream.Collectors;
  80 +
  81 +import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject;
  82 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.CLIENT_NOT_AUTHORIZED;
  83 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEFAULT_TIMEOUT;
  84 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
  85 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_ATTRIBUTES_TOPIC;
  86 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC;
  87 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_OBSERVE;
  88 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.GET_TYPE_OPER_READ;
  89 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_ERROR;
  90 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_INFO;
  91 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.LOG_LW2M_TELEMETRY;
  92 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_EXECUTE;
  93 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
  94 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.SERVICE_CHANNEL;
  95 +import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getAckCallback;
  96 +
  97 +@Slf4j
  98 +@Service("LwM2MTransportService")
  99 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
  100 +public class LwM2MTransportServiceImpl implements LwM2MTransportService {
  101 +
  102 + private ExecutorService executorRegistered;
  103 + private ExecutorService executorUpdateRegistered;
  104 + private ExecutorService executorUnRegistered;
  105 + private LwM2mValueConverterImpl converter;
  106 + protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  107 + protected final Lock writeLock = readWriteLock.writeLock();
  108 +
  109 +
  110 + @Autowired
  111 + private TransportService transportService;
  112 +
  113 + @Autowired
  114 + public LwM2MTransportContextServer context;
  115 +
  116 + @Autowired
  117 + private LwM2MTransportRequest lwM2MTransportRequest;
  118 +
  119 + @Autowired
  120 + LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
  121 +
  122 + @PostConstruct
  123 + public void init() {
  124 + this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getCtxServer().getSessionReportTimeout()), context.getCtxServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
  125 + this.executorRegistered = Executors.newCachedThreadPool(
  126 + new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
  127 + this.executorUpdateRegistered = Executors.newCachedThreadPool(
  128 + new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
  129 + this.executorUnRegistered = Executors.newCachedThreadPool(
  130 + new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
  131 + this.converter = LwM2mValueConverterImpl.getInstance();
  132 + }
  133 +
  134 + /**
  135 + * Start registration device
  136 + * Create session: Map<String <registrationId >, LwM2MClient>
  137 + * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint)
  138 + * 1.1 When we initialize the registration, we register the session by endpoint.
  139 + * 1.2 If the server has incomplete requests (canceling the registration of the previous session),
  140 + * delete the previous session only by the previous registration.getId
  141 + * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId
  142 + * 1.2 Remove from sessions Model by enpPoint
  143 + * Next -> Create new LwM2MClient for current session -> setModelClient...
  144 + *
  145 + * @param lwServer - LeshanServer
  146 + * @param registration - Registration LwM2M Client
  147 + * @param previousObsersations - may be null
  148 + */
  149 + public void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations) {
  150 + executorRegistered.submit(() -> {
  151 + try {
  152 + log.info("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
  153 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.updateInSessionsLwM2MClient(lwServer, registration);
  154 + if (lwM2MClient != null) {
  155 + lwM2MClient.setLwM2MTransportServiceImpl(this);
  156 + lwM2MClient.setSessionUuid(UUID.randomUUID());
  157 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);
  158 + this.setLwM2MClient(lwServer, registration, lwM2MClient);
  159 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  160 + if (sessionInfo != null) {
  161 + lwM2MClient.setDeviceUuid(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
  162 + lwM2MClient.setProfileUuid(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  163 + lwM2MClient.setDeviceName(sessionInfo.getDeviceName());
  164 + lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType());
  165 + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
  166 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
  167 + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
  168 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
  169 + } else {
  170 + log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  171 + }
  172 + } else {
  173 + log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
  174 + }
  175 + } catch (Throwable t) {
  176 + log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);
  177 + }
  178 + });
  179 + }
  180 +
  181 + /**
  182 + * if sessionInfo removed from sessions, then new registerAsyncSession
  183 + * @param lwServer - LeshanServer
  184 + * @param registration - Registration LwM2M Client
  185 + */
  186 + public void updatedReg(LeshanServer lwServer, Registration registration) {
  187 + executorUpdateRegistered.submit(() -> {
  188 + try {
  189 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  190 + if (sessionInfo != null) {
  191 + this.checkInactivity(sessionInfo);
  192 + log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
  193 + } else {
  194 + log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  195 + }
  196 + } catch (Throwable t) {
  197 + log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
  198 + }
  199 + });
  200 + }
  201 +
  202 +
  203 + /**
  204 + * @param registration - Registration LwM2M Client
  205 + * @param observations - All paths observations before unReg
  206 + * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
  207 + */
  208 + public void unReg(Registration registration, Collection<Observation> observations) {
  209 + executorUnRegistered.submit(() -> {
  210 + try {
  211 + this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
  212 + this.closeClientSession(registration);
  213 + } catch (Throwable t) {
  214 + log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
  215 + }
  216 + });
  217 + }
  218 +
  219 + private void closeClientSession(Registration registration) {
  220 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  221 + if (sessionInfo != null) {
  222 + transportService.deregisterSession(sessionInfo);
  223 + this.doCloseSession(sessionInfo);
  224 + lwM2mInMemorySecurityStore.delRemoveSessionAndListener(registration.getId());
  225 + if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) {
  226 + this.syncSessionsAndProfiles();
  227 + }
  228 + log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
  229 + } else {
  230 + log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
  231 + }
  232 + }
  233 +
  234 + public void onSleepingDev(Registration registration) {
  235 + log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
  236 + //TODO: associate endpointId with device information.
  237 + }
  238 +
  239 + /**
  240 + * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
  241 + * * if you need to do long time processing use a dedicated thread pool.
  242 + *
  243 + * @param registration -
  244 + */
  245 + protected void onAwakeDev(Registration registration) {
  246 + log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
  247 + //TODO: associate endpointId with device information.
  248 + }
  249 +
  250 + /**
  251 + * This method is used to sync with sessions
  252 + * Removes a profile if not used in sessions
  253 + */
  254 + private void syncSessionsAndProfiles() {
  255 + Map<UUID, AttrTelemetryObserveValue> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
  256 + .stream()
  257 + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
  258 + profilesClone.forEach((k, v) -> {
  259 + String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
  260 + .stream()
  261 + .filter(e -> e.getValue().getProfileUuid().equals(k))
  262 + .findFirst()
  263 + .map(Map.Entry::getKey) // return the key of the matching entry if found
  264 + .orElse("");
  265 + if (registrationId.isEmpty()) {
  266 + lwM2mInMemorySecurityStore.getProfiles().remove(k);
  267 + }
  268 + });
  269 + }
  270 +
  271 + /**
  272 + * #0 Add new ObjectModel to context
  273 + * Create new LwM2MClient for current session -> setModelClient...
  274 + * #1 Add all ObjectLinks (instance) to control the process of executing requests to the client
  275 + * to get the client model with current values
  276 + * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse
  277 + *
  278 + * @param lwServer - LeshanServer
  279 + * @param registration - Registration LwM2M Client
  280 + * @param lwM2MClient - object with All parameters off client
  281 + */
  282 + private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
  283 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  284 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  285 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  286 + lwM2MClient.getPendingRequests().add(url.getUrl());
  287 + }
  288 + });
  289 + // #2
  290 + Arrays.stream(registration.getObjectLinks()).forEach(url -> {
  291 + LwM2mPath pathIds = new LwM2mPath(url.getUrl());
  292 + if (pathIds.isObjectInstance() && !pathIds.isResource()) {
  293 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
  294 + lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
  295 + }
  296 + });
  297 + }
  298 +
  299 + /**
  300 + * @param registration - Registration LwM2M Client
  301 + * @return - sessionInfo after access connect client
  302 + */
  303 + private SessionInfoProto getValidateSessionInfo(Registration registration) {
  304 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  305 + return getNewSessionInfoProto(lwM2MClient);
  306 +
  307 + }
  308 +
  309 + /**
  310 + *
  311 + * @param registrationId -
  312 + * @return -
  313 + */
  314 + private SessionInfoProto getValidateSessionInfo(String registrationId) {
  315 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
  316 + return getNewSessionInfoProto(lwM2MClient);
  317 + }
  318 +
  319 + private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
  320 + if (lwM2MClient != null) {
  321 + ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
  322 + if (msg == null || msg.getDeviceInfo() == null) {
  323 + log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
  324 + this.closeClientSession(lwM2MClient.getRegistration());
  325 + return null;
  326 + } else {
  327 + return SessionInfoProto.newBuilder()
  328 + .setNodeId(this.context.getNodeId())
  329 + .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
  330 + .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
  331 + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
  332 + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
  333 + .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
  334 + .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
  335 + .setDeviceName(msg.getDeviceInfo().getDeviceName())
  336 + .setDeviceType(msg.getDeviceInfo().getDeviceType())
  337 + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
  338 + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
  339 + .build();
  340 + }
  341 + }
  342 + return null;
  343 + }
  344 +
  345 + /**
  346 + * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe
  347 + * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use
  348 + * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection
  349 + * #2 Start observe
  350 + *
  351 + * @param lwM2MClient - LwM2M Client
  352 + */
  353 +
  354 + public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) {
  355 + // #1
  356 + this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null);
  357 + // #2
  358 + this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration());
  359 +
  360 + }
  361 +
  362 + /**
  363 + * If there is a difference in values between the current resource values and the shared attribute values
  364 + * when the client connects to the server
  365 + * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
  366 + * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
  367 + * #2.2 #1 size == 0 => continue normal process
  368 + *
  369 + * @param lwM2MClient - LwM2M Client
  370 + */
  371 + public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
  372 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
  373 + if (sessionInfo != null) {
  374 + //#1.1 + #1.2
  375 + List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
  376 + if (attrSharedNames.size() > 0) {
  377 + //#2.1
  378 + try {
  379 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
  380 + lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId());
  381 + transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
  382 + } catch (AdaptorException e) {
  383 + log.warn("Failed to decode get attributes request", e);
  384 + }
  385 + }
  386 + // #2.2
  387 + else {
  388 + lwM2MClient.onSuccessOrErrorDelayedRequests(null);
  389 + }
  390 + }
  391 + }
  392 +
  393 + /**
  394 + * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
  395 + * #1 Get path resource by result attributesResponse
  396 + * #1.1 If two names have equal path => last time attribute
  397 + * #2.1 if there is a difference in values between the current resource values and the shared attribute values
  398 + * => sent to client Request Update of value (new value from shared attribute)
  399 + * and LwM2MClient.delayedRequests.add(path)
  400 + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
  401 + *
  402 + * @param attributesResponse -
  403 + * @param sessionInfo -
  404 + */
  405 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
  406 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
  407 + if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) {
  408 + attributesResponse.getSharedAttributeListList().forEach(attr -> {
  409 + String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
  410 + // #1.1
  411 + if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
  412 + lwM2MClient.getDelayedRequests().put(path, attr);
  413 + } else {
  414 + lwM2MClient.getDelayedRequests().put(path, attr);
  415 + }
  416 + });
  417 + // #2.1
  418 + lwM2MClient.getDelayedRequests().forEach((k, v) -> {
  419 + ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
  420 + listV.add(v.getKv());
  421 + this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
  422 + });
  423 + lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());
  424 + if (lwM2MClient.getDelayedRequests().size() == 0) {
  425 + lwM2MClient.onSuccessOrErrorDelayedRequests(null);
  426 + }
  427 + }
  428 + }
  429 +
  430 + private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
  431 + if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {
  432 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  433 + ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
  434 + true);
  435 + }
  436 + }
  437 +
  438 + /**
  439 + * Get names and keyNames from profile shared!!!! attr resources IsWritable
  440 + * @param lwM2MClient -
  441 + * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
  442 + */
  443 + private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
  444 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
  445 + Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
  446 + ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);
  447 +
  448 + ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
  449 + .stream()
  450 + .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
  451 + context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
  452 + .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
  453 +
  454 + Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
  455 + namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
  456 + return new ArrayList<>(namesIsWritable);
  457 + }
  458 +
  459 +
  460 + /**
  461 + * Sent Attribute and Telemetry to Thingsboard
  462 + * #1 - get AttrName/TelemetryName with value:
  463 + * #1.1 from Client
  464 + * #1.2 from LwM2MClient:
  465 + * -- resourceId == path from AttrTelemetryObserveValue.postAttributeProfile/postTelemetryProfile/postObserveProfile
  466 + * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
  467 + * #2 - set Attribute/Telemetry
  468 + *
  469 + * @param registration - Registration LwM2M Client
  470 + */
  471 + private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
  472 + JsonObject attributes = new JsonObject();
  473 + JsonObject telemetries = new JsonObject();
  474 + if (start) {
  475 + // #1.1
  476 + JsonObject attributeClient = this.getAttributeClient(registration);
  477 + if (attributeClient != null) {
  478 + attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
  479 + }
  480 + }
  481 + // #1.2
  482 + CountDownLatch cancelLatch = new CountDownLatch(1);
  483 + this.getParametersFromProfile(attributes, telemetries, registration, paths);
  484 + cancelLatch.countDown();
  485 + try {
  486 + cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
  487 + } catch (InterruptedException e) {
  488 + log.error("[{}] updateAttrTelemetry", e.toString());
  489 + }
  490 + if (attributes.getAsJsonObject().entrySet().size() > 0)
  491 + this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
  492 + if (telemetries.getAsJsonObject().entrySet().size() > 0)
  493 + this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
  494 + }
  495 +
  496 + /**
  497 + * get AttrName/TelemetryName with value from Client
  498 + *
  499 + * @param registration -
  500 + * @return - JsonObject, format: {name: value}}
  501 + */
  502 + private JsonObject getAttributeClient(Registration registration) {
  503 + if (registration.getAdditionalRegistrationAttributes().size() > 0) {
  504 + JsonObject resNameValues = new JsonObject();
  505 + registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);
  506 + return resNameValues;
  507 + }
  508 + return null;
  509 + }
  510 +
  511 + /**
  512 + * @param attributes - new JsonObject
  513 + * @param telemetry - new JsonObject
  514 + * @param registration - Registration LwM2M Client
  515 + * result: add to JsonObject those resources to which the user is subscribed and they have a value
  516 + * if path==null add All resources else only one
  517 + * (attributes/telemetry): new {name(Attr/Telemetry):value}
  518 + */
  519 + private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
  520 + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
  521 + attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {
  522 + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
  523 + if (pathIds.isResource()) {
  524 + if (path == null || path.contains(p.getAsString())) {
  525 + this.addParameters(p.getAsString().toString(), attributes, registration);
  526 + }
  527 + }
  528 + });
  529 + attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {
  530 + LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
  531 + if (pathIds.isResource()) {
  532 + if (path == null || path.contains(p.getAsString())) {
  533 + this.addParameters(p.getAsString().toString(), telemetry, registration);
  534 + }
  535 + }
  536 + });
  537 + }
  538 +
  539 + /**
  540 + * @param parameters - JsonObject attributes/telemetry
  541 + * @param registration - Registration LwM2M Client
  542 + */
  543 + private void addParameters(String path, JsonObject parameters, Registration registration) {
  544 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
  545 + JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
  546 + String resName = String.valueOf(names.get(path));
  547 + if (resName != null && !resName.isEmpty()) {
  548 + try {
  549 + String resValue = this.getResourceValueToString(lwM2MClient, path);
  550 + if (resValue != null) {
  551 + parameters.addProperty(resName, resValue);
  552 + }
  553 + } catch (Exception e) {
  554 + log.error(e.getStackTrace().toString());
  555 + }
  556 + }
  557 + }
  558 +
  559 + /**
  560 + * Prepare Sent to Thigsboard callback - Attribute or Telemetry
  561 + *
  562 + * @param msg - JsonArray: [{name: value}]
  563 + * @param topicName - Api Attribute or Telemetry
  564 + * @param registration - Id of Registration LwM2M Client
  565 + */
  566 + public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
  567 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
  568 + if (sessionInfo != null) {
  569 + context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
  570 + } else {
  571 + log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
  572 + }
  573 + }
  574 +
  575 + /**
  576 + * Start observe
  577 + * #1 - Analyze:
  578 + * #1.1 path in observe == (attribute or telemetry)
  579 + * #2 Analyze after sent request (response):
  580 + * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation
  581 + * #2.2 Next: ObservationListener.onResponse *
  582 + *
  583 + * @param lwServer - LeshanServer
  584 + * @param registration - Registration LwM2M Client
  585 + */
  586 + private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
  587 + if (lwServer.getObservationService().getObservations(registration).size() > 0) {
  588 + this.setCancelObservations(lwServer, registration);
  589 + }
  590 + UUID profileUUid = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid();
  591 + AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(profileUUid);
  592 + attrTelemetryObserveValue.getPostObserveProfile().forEach(p -> {
  593 + // #1.1
  594 + String target = (getValidateObserve(attrTelemetryObserveValue.getPostAttributeProfile(), p.getAsString().toString())) ?
  595 + p.getAsString().toString() : (getValidateObserve(attrTelemetryObserveValue.getPostTelemetryProfile(), p.getAsString().toString())) ?
  596 + p.getAsString().toString() : null;
  597 + if (target != null) {
  598 + // #2
  599 + if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) {
  600 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
  601 + null, null, null, null, this.context.getCtxServer().getTimeout(),
  602 + false);
  603 + }
  604 + }
  605 + });
  606 + }
  607 +
  608 + public void setCancelObservations(LeshanServer lwServer, Registration registration) {
  609 + if (registration != null) {
  610 + Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
  611 + observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
  612 + }
  613 + }
  614 +
  615 + /**
  616 + * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
  617 + * At server side this will not remove the observation from the observation store, to do it you need to use
  618 + * {@code ObservationService#cancelObservation()}
  619 + */
  620 + public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
  621 + CountDownLatch cancelLatch = new CountDownLatch(1);
  622 + lwServer.getObservationService().cancelObservations(registration, path);
  623 + cancelLatch.countDown();
  624 + try {
  625 + cancelLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
  626 + } catch (InterruptedException e) {
  627 + log.error("", e);
  628 + }
  629 + }
  630 +
  631 + /**
  632 + * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile
  633 + * @param path - recourse from postObserveProfile
  634 + * @return rez - true if path observe is in attribute/telemetry
  635 + */
  636 + private boolean getValidateObserve(JsonElement parameters, String path) {
  637 + AtomicBoolean rez = new AtomicBoolean(false);
  638 + if (parameters.isJsonArray()) {
  639 + parameters.getAsJsonArray().forEach(p -> {
  640 + if (p.getAsString().toString().equals(path)) rez.set(true);
  641 + }
  642 + );
  643 + } else if (parameters.isJsonObject()) {
  644 + rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())
  645 + .filter(path::equals).findAny().orElse(null) != null);
  646 + }
  647 + return rez.get();
  648 + }
  649 +
  650 + /**
  651 + * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
  652 + *
  653 + * @param registration - Registration LwM2M Client
  654 + * @param path - observe
  655 + * @param response - observe
  656 + */
  657 +
  658 + public void onObservationResponse(Registration registration, String path, ReadResponse response) {
  659 + if (response.getContent() != null) {
  660 + if (response.getContent() instanceof LwM2mObject) {
  661 +// LwM2mObject content = (LwM2mObject) response.getContent();
  662 + } else if (response.getContent() instanceof LwM2mObjectInstance) {
  663 +// LwM2mObjectInstance content = (LwM2mObjectInstance) response.getContent();
  664 + } else if (response.getContent() instanceof LwM2mSingleResource) {
  665 + LwM2mSingleResource content = (LwM2mSingleResource) response.getContent();
  666 + this.onObservationSetResourcesValue(registration, content.getValue(), null, path);
  667 + } else if (response.getContent() instanceof LwM2mMultipleResource) {
  668 + LwM2mMultipleResource content = (LwM2mMultipleResource) response.getContent();
  669 + this.onObservationSetResourcesValue(registration, null, content.getValues(), path);
  670 + }
  671 + }
  672 + }
  673 +
  674 + /**
  675 + * Sending observe value of resources to thingsboard
  676 + * #1 Return old Value Resource from LwM2MClient
  677 + * #2 Update new Resources (replace old Resource Value on new Resource Value)
  678 + *
  679 + * @param registration - Registration LwM2M Client
  680 + * @param value - LwM2mSingleResource response.getContent()
  681 + * @param values - LwM2mSingleResource response.getContent()
  682 + * @param path - resource
  683 + */
  684 + private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {
  685 + boolean isChange = false;
  686 + try {
  687 + writeLock.lock();
  688 + try {
  689 + // #1
  690 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  691 + LwM2mPath pathIds = new LwM2mPath(path);
  692 + log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), pathIds, value, values);
  693 + ResourceModel.Type resModelType = context.getCtxServer().getResourceModelType(registration, pathIds);
  694 + ResourceValue resValueOld = lwM2MClient.getResources().get(path);
  695 + // #2
  696 + if (resValueOld.isMultiInstances() && !values.toString().equals(resValueOld.getResourceValue().toString())) {
  697 + ResourceValue resourceValue = new ResourceValue(values, null, true);
  698 + lwM2MClient.getResources().put(path, resourceValue);
  699 + isChange = true;
  700 + } else if (!LwM2MTransportHandler.equalsResourceValue(resValueOld.getValue(), value, resModelType, pathIds)) {
  701 + ResourceValue resourceValue = new ResourceValue(null, value, false);
  702 + lwM2MClient.getResources().put(path, resourceValue);
  703 + isChange = true;
  704 + }
  705 + } finally {
  706 + writeLock.unlock();
  707 + }
  708 + }
  709 + catch (Exception e) {
  710 + log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}] [{}]", path, value, e.toString());
  711 + }
  712 + if (isChange) {
  713 + Set<String> paths = new HashSet<>();
  714 + paths.add(path);
  715 + this.updateAttrTelemetry(registration, false, paths);
  716 + }
  717 + }
  718 +
  719 + /**
  720 + * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
  721 + * config attr/telemetry... in profile
  722 + */
  723 + public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {
  724 + log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
  725 + }
  726 +
  727 + /**
  728 + * Update - sent request in change value resources in Client
  729 + * Path to resources from profile equal keyName or from ModelObject equal name
  730 + * Only for resources: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
  731 + * Delete - nothing *
  732 + *
  733 + * @param msg -
  734 + */
  735 + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  736 + if (msg.getSharedUpdatedCount() > 0) {
  737 + JsonElement el = JsonConverter.toJson(msg);
  738 + el.getAsJsonObject().entrySet().forEach(de -> {
  739 + String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
  740 + String value = de.getValue().getAsString();
  741 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
  742 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  743 + ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
  744 + if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
  745 + if (resourceModel != null && resourceModel.operations.isWritable()) {
  746 + lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
  747 + ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
  748 + false);
  749 + } else {
  750 + log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
  751 + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
  752 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  753 + }
  754 + } else {
  755 + log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
  756 + String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value);
  757 + this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  758 + }
  759 + });
  760 + } else if (msg.getSharedDeletedCount() > 0) {
  761 + log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
  762 + }
  763 + }
  764 +
  765 + /**
  766 + * Get path to resource from profile equal keyName or from ModelObject equal name
  767 + * Only for resource: isWritable && isPresent as attribute in profile -> AttrTelemetryObserveValue (format: CamelCase)
  768 + *
  769 + * @param sessionInfo -
  770 + * @param name -
  771 + * @return path if path isPresent in postProfile
  772 + */
  773 + private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
  774 + String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
  775 +// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name);
  776 + return !profilePath.isEmpty() ? profilePath : null;
  777 + }
  778 +
  779 + /**
  780 + * @param profile -
  781 + * @param path -
  782 + * @return true if path isPresent in postAttributeProfile
  783 + */
  784 + private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) {
  785 + Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
  786 + return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
  787 + }
  788 +
  789 + /**
  790 + * @param profile -
  791 + * @param path -
  792 + * @return true if path isPresent in postAttributeProfile
  793 + */
  794 + private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) {
  795 + Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), Set.class);
  796 + return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
  797 + }
  798 +
  799 +
  800 + /**
  801 + * Get path to resource from profile equal keyName
  802 + *
  803 + * @param sessionInfo -
  804 + * @param name -
  805 + * @return -
  806 + */
  807 + private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
  808 + AttrTelemetryObserveValue profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
  809 + return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
  810 + .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
  811 + .orElse("");
  812 + }
  813 +
  814 + /**
  815 + * Update resource (attribute) value on thingsboard after update value in client
  816 + *
  817 + * @param registration -
  818 + * @param path -
  819 + * @param request -
  820 + */
  821 + public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
  822 + ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path));
  823 + if (resource.multiple) {
  824 + this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);
  825 + } else {
  826 + this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);
  827 + }
  828 + if (isDelayedUpdate) lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null)
  829 + .onSuccessOrErrorDelayedRequests(request.getPath().toString());
  830 + }
  831 +
  832 + /**
  833 + * @param sessionInfo -
  834 + * @param deviceProfile -
  835 + */
  836 + public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
  837 + Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
  838 + .stream()
  839 + .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
  840 + .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
  841 + if (registrationIds.size() > 0) {
  842 + this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
  843 + }
  844 + }
  845 +
  846 + /**
  847 + * @param sessionInfo -
  848 + * @param device -
  849 + * @param deviceProfileOpt -
  850 + */
  851 + public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  852 + Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
  853 + .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
  854 + .map(Map.Entry::getKey)
  855 + .findFirst();
  856 + registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
  857 + }
  858 +
  859 + /**
  860 + * Update parameters device in LwM2MClient
  861 + * If new deviceProfile != old deviceProfile => update deviceProfile
  862 + *
  863 + * @param registrationId -
  864 + * @param device -
  865 + */
  866 + private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
  867 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
  868 + lwM2MClient.setDeviceName(device.getName());
  869 + if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
  870 + Set<String> registrationIds = new HashSet<>();
  871 + registrationIds.add(registrationId);
  872 + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
  873 + }
  874 +
  875 + lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
  876 + }
  877 +
  878 + /**
  879 + * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName)
  880 + * #2 Update in lwM2MClient: ...Profile if changes from update device
  881 + * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName)
  882 + * #3.1 Attribute isChange (add&del)
  883 + * #3.2 Telemetry isChange (add&del)
  884 + * #3.3 KeyName isChange (add)
  885 + * #4 update
  886 + * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and sent Value to thingsboard
  887 + * #4.2 del
  888 + * -- if add attributes includes del telemetry - result del for observe
  889 + * #5
  890 + * #5.1 Observe isChange (add&del)
  891 + * #5.2 Observe.add
  892 + * -- path Attr/Telemetry includes newObserve and does not include oldObserve: sent Request observe to Client
  893 + * #5.3 Observe.del
  894 + * -- different between newObserve and oldObserve: sent Request cancel observe to client
  895 + *
  896 + * @param registrationIds -
  897 + * @param deviceProfile -
  898 + */
  899 + private void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) {
  900 +
  901 + AttrTelemetryObserveValue attrTelemetryObserveValueOld = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  902 + if (lwM2mInMemorySecurityStore.addUpdateProfileParameters(deviceProfile)) {
  903 +
  904 + // #1
  905 + JsonArray attributeOld = attrTelemetryObserveValueOld.getPostAttributeProfile();
  906 + Set attributeSetOld = new Gson().fromJson(attributeOld, Set.class);
  907 + JsonArray telemetryOld = attrTelemetryObserveValueOld.getPostTelemetryProfile();
  908 + Set telemetrySetOld = new Gson().fromJson(telemetryOld, Set.class);
  909 + JsonArray observeOld = attrTelemetryObserveValueOld.getPostObserveProfile();
  910 + JsonObject keyNameOld = attrTelemetryObserveValueOld.getPostKeyNameProfile();
  911 +
  912 + AttrTelemetryObserveValue attrTelemetryObserveValueNew = lwM2mInMemorySecurityStore.getProfiles().get(deviceProfile.getUuidId());
  913 + JsonArray attributeNew = attrTelemetryObserveValueNew.getPostAttributeProfile();
  914 + Set attributeSetNew = new Gson().fromJson(attributeNew, Set.class);
  915 + JsonArray telemetryNew = attrTelemetryObserveValueNew.getPostTelemetryProfile();
  916 + Set telemetrySetNew = new Gson().fromJson(telemetryNew, Set.class);
  917 + JsonArray observeNew = attrTelemetryObserveValueNew.getPostObserveProfile();
  918 + JsonObject keyNameNew = attrTelemetryObserveValueNew.getPostKeyNameProfile();
  919 +
  920 + // #3
  921 + ResultsAnalyzerParameters sentAttrToThingsboard = new ResultsAnalyzerParameters();
  922 + // #3.1
  923 + if (!attributeOld.equals(attributeNew)) {
  924 + ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, Set.class), attributeSetNew);
  925 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
  926 + sentAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
  927 + }
  928 + // #3.2
  929 + if (!attributeOld.equals(attributeNew)) {
  930 + ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, Set.class), telemetrySetNew);
  931 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
  932 + sentAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
  933 + }
  934 + // #3.3
  935 + if (!keyNameOld.equals(keyNameNew)) {
  936 + ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), ConcurrentHashMap.class),
  937 + new Gson().fromJson(keyNameNew.toString(), ConcurrentHashMap.class));
  938 + sentAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
  939 + }
  940 +
  941 + // #4.1 add
  942 + if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
  943 + // update value in Resources
  944 + registrationIds.forEach(registrationId -> {
  945 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
  946 + LeshanServer lwServer = lwM2MClient.getLwServer();
  947 + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
  948 + log.warn("[{}] # 4.1", registration.getEndpoint());
  949 + this.updateResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
  950 + // sent attr/telemetry to tingsboard for new path
  951 + this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd());
  952 + });
  953 + }
  954 + // #4.2 del
  955 + if (sentAttrToThingsboard.getPathPostParametersDel().size() > 0) {
  956 + ResultsAnalyzerParameters sentAttrToThingsboardDel = this.getAnalyzerParameters(sentAttrToThingsboard.getPathPostParametersAdd(), sentAttrToThingsboard.getPathPostParametersDel());
  957 + sentAttrToThingsboard.setPathPostParametersDel(sentAttrToThingsboardDel.getPathPostParametersDel());
  958 + }
  959 +
  960 + // #5.1
  961 + if (!observeOld.equals(observeNew)) {
  962 + Set observeSetOld = new Gson().fromJson(observeOld, Set.class);
  963 + Set observeSetNew = new Gson().fromJson(observeNew, Set.class);
  964 + //#5.2 add
  965 + // path Attr/Telemetry includes newObserve
  966 + attributeSetOld.addAll(telemetrySetOld);
  967 + ResultsAnalyzerParameters sentObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe
  968 + attributeSetNew.addAll(telemetrySetNew);
  969 + ResultsAnalyzerParameters sentObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe
  970 + // does not include oldObserve
  971 + ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd());
  972 + // sent Request observe to Client
  973 + registrationIds.forEach(registrationId -> {
  974 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(null, registrationId);
  975 + LeshanServer lwServer = lwM2MClient.getLwServer();
  976 + Registration registration = lwM2mInMemorySecurityStore.getByRegistration(registrationId);
  977 + log.warn("[{}] # 5.1", registration.getEndpoint());
  978 + this.updateResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
  979 + // 5.3 del
  980 + // sent Request cancel observe to Client
  981 + this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel());
  982 + });
  983 + }
  984 + }
  985 + }
  986 +
  987 + /**
  988 + * Compare old list with new list after change AttrTelemetryObserve in config Profile
  989 + *
  990 + * @param parametersOld -
  991 + * @param parametersNew -
  992 + * @return ResultsAnalyzerParameters: add && new
  993 + */
  994 + private ResultsAnalyzerParameters getAnalyzerParameters(Set<String> parametersOld, Set<String> parametersNew) {
  995 + ResultsAnalyzerParameters analyzerParameters = null;
  996 + if (!parametersOld.equals(parametersNew)) {
  997 + analyzerParameters = new ResultsAnalyzerParameters();
  998 + analyzerParameters.setPathPostParametersAdd(parametersNew
  999 + .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet()));
  1000 + analyzerParameters.setPathPostParametersDel(parametersOld
  1001 + .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet()));
  1002 + }
  1003 + return analyzerParameters;
  1004 + }
  1005 +
  1006 + private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
  1007 + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
  1008 + Set<String> paths = keyNameNew.entrySet()
  1009 + .stream()
  1010 + .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
  1011 + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
  1012 + analyzerParameters.setPathPostParametersAdd(paths);
  1013 + return analyzerParameters;
  1014 + }
  1015 +
  1016 + private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {
  1017 + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
  1018 + analyzerParameters.setPathPostParametersAdd(parametersObserve
  1019 + .stream().filter(parameters::contains).collect(Collectors.toSet()));
  1020 + return analyzerParameters;
  1021 + }
  1022 +
  1023 + /**
  1024 + * Update Resource value after change RezAttrTelemetry in config Profile
  1025 + * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
  1026 + *
  1027 + * @param lwServer - LeshanServer
  1028 + * @param registration - Registration LwM2M Client
  1029 + * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
  1030 + */
  1031 + private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) {
  1032 + targets.forEach(target -> {
  1033 + LwM2mPath pathIds = new LwM2mPath(target);
  1034 + if (pathIds.isResource()) {
  1035 + if (GET_TYPE_OPER_READ.equals(typeOper)) {
  1036 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
  1037 + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
  1038 + false);
  1039 + } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
  1040 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
  1041 + null, null, null, null, this.context.getCtxServer().getTimeout(),
  1042 + false);
  1043 + }
  1044 + }
  1045 + });
  1046 + }
  1047 +
  1048 + private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {
  1049 + LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
  1050 + paramAnallyzer.forEach(p -> {
  1051 + if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) {
  1052 + this.setCancelObservationRecourse(lwServer, registration, p);
  1053 + }
  1054 + }
  1055 + );
  1056 + }
  1057 +
  1058 + private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
  1059 + ResourceValue resourceValue = null;
  1060 + if (pathIds.isResource()) {
  1061 + resourceValue = lwM2MClient.getResources().get(pathIds.toString());
  1062 + }
  1063 + return resourceValue;
  1064 + }
  1065 +
  1066 + /**
  1067 + * Trigger Server path = "/1/0/8"
  1068 + *
  1069 + * Trigger bootStrap path = "/1/0/9" - have to implemented on client
  1070 + */
  1071 + public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
  1072 + lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
  1073 + ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
  1074 + false);
  1075 + }
  1076 +
  1077 + /**
  1078 + * Session device in thingsboard is closed
  1079 + *
  1080 + * @param sessionInfo - lwm2m client
  1081 + */
  1082 + private void doCloseSession(SessionInfoProto sessionInfo) {
  1083 + TransportProtos.SessionEvent event = SessionEvent.CLOSED;
  1084 + TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
  1085 + .setSessionType(TransportProtos.SessionType.ASYNC)
  1086 + .setEvent(event).build();
  1087 + transportService.process(sessionInfo, msg, null);
  1088 + }
  1089 +
  1090 + /**
  1091 + * Deregister session in transport
  1092 + *
  1093 + * @param sessionInfo - lwm2m client
  1094 + */
  1095 + public void doDisconnect(SessionInfoProto sessionInfo) {
  1096 + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
  1097 + transportService.deregisterSession(sessionInfo);
  1098 + }
  1099 +
  1100 + private void checkInactivityAndReportActivity() {
  1101 + lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
  1102 + }
  1103 +
  1104 + /**
  1105 + * if sessionInfo removed from sessions, then new registerAsyncSession
  1106 + * @param sessionInfo -
  1107 + */
  1108 + private void checkInactivity(SessionInfoProto sessionInfo) {
  1109 + if (transportService.reportActivity(sessionInfo) == null) {
  1110 + transportService.registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(this, sessionInfo));
  1111 + }
  1112 + }
  1113 +
  1114 + public void sentLogsToThingsboard(String msg, Registration registration) {
  1115 + if (msg != null) {
  1116 + JsonObject telemetries = new JsonObject();
  1117 + telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
  1118 + this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration);
  1119 + }
  1120 + }
  1121 +
  1122 + /**
  1123 + * @param path - path resource
  1124 + * @return - value of Resource or null
  1125 + */
  1126 + private String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
  1127 + LwM2mPath pathIds = new LwM2mPath(path);
  1128 + ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds);
  1129 + return (resourceValue == null) ? null :
  1130 + (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
  1131 + }
  1132 +}
@@ -24,17 +24,25 @@ import org.eclipse.leshan.server.queue.PresenceListener; @@ -24,17 +24,25 @@ import org.eclipse.leshan.server.queue.PresenceListener;
24 import org.eclipse.leshan.server.registration.Registration; 24 import org.eclipse.leshan.server.registration.Registration;
25 import org.eclipse.leshan.server.registration.RegistrationListener; 25 import org.eclipse.leshan.server.registration.RegistrationListener;
26 import org.eclipse.leshan.server.registration.RegistrationUpdate; 26 import org.eclipse.leshan.server.registration.RegistrationUpdate;
  27 +import org.springframework.beans.factory.annotation.Autowired;
  28 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  29 +import org.springframework.stereotype.Component;
27 30
28 import java.util.Collection; 31 import java.util.Collection;
29 32
30 @Slf4j 33 @Slf4j
  34 +@Component()
  35 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
31 public class LwM2mServerListener { 36 public class LwM2mServerListener {
  37 +
32 private LeshanServer lhServer; 38 private LeshanServer lhServer;
33 - private LwM2MTransportService service;  
34 39
35 - public LwM2mServerListener(LeshanServer lhServer, LwM2MTransportService service) { 40 + @Autowired
  41 + private LwM2MTransportServiceImpl service;
  42 +
  43 + public LwM2mServerListener init(LeshanServer lhServer) {
36 this.lhServer = lhServer; 44 this.lhServer = lhServer;
37 - this.service = service; 45 + return this;
38 } 46 }
39 47
40 public final RegistrationListener registrationListener = new RegistrationListener() { 48 public final RegistrationListener registrationListener = new RegistrationListener() {
@@ -96,8 +104,7 @@ public class LwM2mServerListener { @@ -96,8 +104,7 @@ public class LwM2mServerListener {
96 try { 104 try {
97 service.onObservationResponse(registration, observation.getPath().toString(), response); 105 service.onObservationResponse(registration, observation.getPath().toString(), response);
98 } catch (Exception e) { 106 } catch (Exception e) {
99 - e.printStackTrace();  
100 - log.error("onResponse"); 107 + log.error("[{}] onResponse", e.toString());
101 108
102 } 109 }
103 } 110 }
@@ -27,7 +27,7 @@ import org.eclipse.leshan.server.registration.Registration; @@ -27,7 +27,7 @@ import org.eclipse.leshan.server.registration.Registration;
27 import org.eclipse.leshan.server.security.SecurityInfo; 27 import org.eclipse.leshan.server.security.SecurityInfo;
28 import org.thingsboard.server.gen.transport.TransportProtos; 28 import org.thingsboard.server.gen.transport.TransportProtos;
29 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; 29 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
30 -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportService; 30 +import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportServiceImpl;
31 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; 31 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
32 32
33 import java.util.Map; 33 import java.util.Map;
@@ -48,7 +48,7 @@ public class LwM2MClient implements Cloneable { @@ -48,7 +48,7 @@ public class LwM2MClient implements Cloneable {
48 private UUID sessionUuid; 48 private UUID sessionUuid;
49 private UUID profileUuid; 49 private UUID profileUuid;
50 private LeshanServer lwServer; 50 private LeshanServer lwServer;
51 - private LwM2MTransportService lwM2MTransportService; 51 + private LwM2MTransportServiceImpl lwM2MTransportServiceImpl;
52 private Registration registration; 52 private Registration registration;
53 private ValidateDeviceCredentialsResponseMsg credentialsResponse; 53 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
54 private Map<String, String> attributes; 54 private Map<String, String> attributes;
@@ -92,7 +92,7 @@ public class LwM2MClient implements Cloneable { @@ -92,7 +92,7 @@ public class LwM2MClient implements Cloneable {
92 this.pendingRequests.remove(path); 92 this.pendingRequests.remove(path);
93 if (this.pendingRequests.size() == 0) { 93 if (this.pendingRequests.size() == 0) {
94 this.initValue(); 94 this.initValue();
95 - this.lwM2MTransportService.putDelayedUpdateResourcesThingsboard(this); 95 + this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this);
96 } 96 }
97 } 97 }
98 98
@@ -123,7 +123,7 @@ public class LwM2MClient implements Cloneable { @@ -123,7 +123,7 @@ public class LwM2MClient implements Cloneable {
123 public void onSuccessOrErrorDelayedRequests(String path) { 123 public void onSuccessOrErrorDelayedRequests(String path) {
124 if (path != null) this.delayedRequests.remove(path); 124 if (path != null) this.delayedRequests.remove(path);
125 if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) { 125 if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) {
126 - this.lwM2MTransportService.updatesAndSentModelParameter(this); 126 + this.lwM2MTransportServiceImpl.updatesAndSentModelParameter(this);
127 } 127 }
128 } 128 }
129 129
1 -/**  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.transport.lwm2m.server.secure;  
17 -  
18 -import lombok.Data;  
19 -import lombok.extern.slf4j.Slf4j;  
20 -import org.eclipse.leshan.core.util.Hex;  
21 -import org.eclipse.leshan.server.californium.LeshanServerBuilder;  
22 -import org.eclipse.leshan.server.redis.RedisRegistrationStore;  
23 -import org.eclipse.leshan.server.redis.RedisSecurityStore;  
24 -import org.eclipse.leshan.server.security.DefaultAuthorizer;  
25 -import org.eclipse.leshan.server.security.EditableSecurityStore;  
26 -import org.eclipse.leshan.server.security.SecurityChecker;  
27 -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;  
28 -import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;  
29 -import redis.clients.jedis.Jedis;  
30 -import redis.clients.jedis.JedisPool;  
31 -import redis.clients.jedis.util.Pool;  
32 -  
33 -import java.math.BigInteger;  
34 -import java.net.URI;  
35 -import java.net.URISyntaxException;  
36 -import java.security.KeyStore;  
37 -import java.security.PublicKey;  
38 -import java.security.PrivateKey;  
39 -import java.security.AlgorithmParameters;  
40 -import java.security.KeyFactory;  
41 -import java.security.GeneralSecurityException;  
42 -import java.security.KeyStoreException;  
43 -import java.security.cert.X509Certificate;  
44 -import java.security.interfaces.ECPublicKey;  
45 -import java.security.spec.ECGenParameterSpec;  
46 -import java.security.spec.ECParameterSpec;  
47 -import java.security.spec.ECPoint;  
48 -import java.security.spec.ECPrivateKeySpec;  
49 -import java.security.spec.ECPublicKeySpec;  
50 -import java.security.spec.KeySpec;  
51 -import java.util.Arrays;  
52 -  
53 -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.REDIS;  
54 -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;  
55 -  
56 -  
57 -@Slf4j  
58 -@Data  
59 -public class LwM2MSetSecurityStoreServer {  
60 -  
61 - private KeyStore keyStore;  
62 - private X509Certificate certificate;  
63 - private PublicKey publicKey;  
64 - private PrivateKey privateKey;  
65 - private LwM2MTransportContextServer context;  
66 - private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;  
67 -  
68 - private LeshanServerBuilder builder;  
69 - EditableSecurityStore securityStore;  
70 -  
71 - public LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MTransportContextServer context, LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore, LwM2MSecurityMode dtlsMode) {  
72 - this.builder = builder;  
73 - this.context = context;  
74 - this.lwM2mInMemorySecurityStore = lwM2mInMemorySecurityStore;  
75 - /** Set securityStore with new registrationStore */  
76 - switch (dtlsMode) {  
77 - /** Use PSK only */  
78 - case PSK:  
79 - generatePSK_RPK();  
80 - if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {  
81 - builder.setPrivateKey(this.privateKey);  
82 - builder.setPublicKey(null);  
83 - getParamsPSK();  
84 - }  
85 - break;  
86 - /** Use RPK only */  
87 - case RPK:  
88 - generatePSK_RPK();  
89 - if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&  
90 - this.privateKey != null && this.privateKey.getEncoded().length > 0) {  
91 - builder.setPublicKey(this.publicKey);  
92 - builder.setPrivateKey(this.privateKey);  
93 - getParamsRPK();  
94 - }  
95 - break;  
96 - /** Use x509 only */  
97 - case X509:  
98 - setServerWithX509Cert();  
99 - break;  
100 - /** No security */  
101 - case NO_SEC:  
102 - builder.setTrustedCertificates(new X509Certificate[0]);  
103 - break;  
104 - /** Use x509 with EST */  
105 - case X509_EST:  
106 - // TODO support sentinel pool and make pool configurable  
107 - break;  
108 - case REDIS:  
109 - /**  
110 - * Set securityStore with new registrationStore (if use redis store)  
111 - * Connect to redis  
112 - */  
113 - Pool<Jedis> jedis = null;  
114 - try {  
115 - jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl()));  
116 - securityStore = new RedisSecurityStore(jedis);  
117 - builder.setRegistrationStore(new RedisRegistrationStore(jedis));  
118 - } catch (URISyntaxException e) {  
119 - e.printStackTrace();  
120 - }  
121 - break;  
122 - default:  
123 - }  
124 -  
125 - /** Set securityStore with new registrationStore (if not redis)*/  
126 - if (dtlsMode.code < REDIS.code) {  
127 - securityStore = lwM2mInMemorySecurityStore;  
128 - if (dtlsMode == X509) {  
129 - builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {  
130 - @Override  
131 - protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,  
132 - String expectedX509CommonName) {  
133 - return endpoint.startsWith(expectedX509CommonName);  
134 - }  
135 - }));  
136 - }  
137 - }  
138 -  
139 - /** Set securityStore with new registrationStore */  
140 - builder.setSecurityStore(securityStore);  
141 - }  
142 -  
143 - private void generatePSK_RPK() {  
144 - try {  
145 - /** Get Elliptic Curve Parameter spec for secp256r1 */  
146 - AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");  
147 - algoParameters.init(new ECGenParameterSpec("secp256r1"));  
148 - ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);  
149 - if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) {  
150 - /** Get point values */  
151 - byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray());  
152 - byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray());  
153 - /** Create key specs */  
154 - KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),  
155 - parameterSpec);  
156 - /** Get keys */  
157 - this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);  
158 - }  
159 - if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) {  
160 - /** Get point values */  
161 - byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray());  
162 - /** Create key specs */  
163 - KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);  
164 - /** Get keys */  
165 - this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);  
166 - }  
167 - } catch (GeneralSecurityException | IllegalArgumentException e) {  
168 - log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());  
169 - throw new RuntimeException(e);  
170 - }  
171 - }  
172 -  
173 - private void setServerWithX509Cert() {  
174 - try {  
175 - if (this.context.getCtxServer().getKeyStoreValue() != null) {  
176 - setBuilderX509();  
177 - X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias());  
178 - if (rootCAX509Cert != null) {  
179 - X509Certificate[] trustedCertificates = new X509Certificate[1];  
180 - trustedCertificates[0] = rootCAX509Cert;  
181 - builder.setTrustedCertificates(trustedCertificates);  
182 - } else {  
183 - /** by default trust all */  
184 - builder.setTrustedCertificates(new X509Certificate[0]);  
185 - }  
186 - }  
187 - else {  
188 - /** by default trust all */  
189 - this.builder.setTrustedCertificates(new X509Certificate[0]);  
190 - log.error("Unable to load X509 files for LWM2MServer");  
191 - }  
192 - } catch (KeyStoreException ex) {  
193 - log.error("[{}] Unable to load X509 files server", ex.getMessage());  
194 - }  
195 - }  
196 -  
197 - private void setBuilderX509() {  
198 - /**  
199 - * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE  
200 - * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks  
201 - */  
202 - try {  
203 - X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias());  
204 - PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());  
205 - this.builder.setPrivateKey(privateKey);  
206 - this.builder.setCertificateChain(new X509Certificate[]{serverCertificate});  
207 - } catch (Exception ex) {  
208 - log.error("[{}] Unable to load KeyStore files server", ex.getMessage());  
209 - }  
210 - }  
211 -  
212 - private void getParamsPSK() {  
213 - log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]",  
214 - Hex.encodeHexString(this.privateKey.getEncoded()),  
215 - this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort()));  
216 - }  
217 -  
218 - private void getParamsRPK() {  
219 - if (this.publicKey instanceof ECPublicKey) {  
220 - /** Get x coordinate */  
221 - byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();  
222 - if (x[0] == 0)  
223 - x = Arrays.copyOfRange(x, 1, x.length);  
224 -  
225 - /** Get Y coordinate */  
226 - byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();  
227 - if (y[0] == 0)  
228 - y = Arrays.copyOfRange(y, 1, y.length);  
229 -  
230 - /** Get Curves params */  
231 - String params = ((ECPublicKey) this.publicKey).getParams().toString();  
232 - log.info(  
233 - " \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",  
234 - params, Hex.encodeHexString(x), Hex.encodeHexString(y),  
235 - Hex.encodeHexString(this.publicKey.getEncoded()),  
236 - Hex.encodeHexString(this.privateKey.getEncoded()));  
237 - } else {  
238 - throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");  
239 - }  
240 - }  
241 -}  
@@ -25,32 +25,32 @@ import org.eclipse.leshan.server.security.SecurityInfo; @@ -25,32 +25,32 @@ import org.eclipse.leshan.server.security.SecurityInfo;
25 import org.eclipse.leshan.server.security.SecurityStoreListener; 25 import org.eclipse.leshan.server.security.SecurityStoreListener;
26 import org.springframework.beans.factory.annotation.Autowired; 26 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 27 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
28 -import org.springframework.stereotype.Component; 28 +import org.springframework.stereotype.Service;
29 import org.thingsboard.server.common.data.DeviceProfile; 29 import org.thingsboard.server.common.data.DeviceProfile;
30 import org.thingsboard.server.gen.transport.TransportProtos; 30 import org.thingsboard.server.gen.transport.TransportProtos;
31 -import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo; 31 +import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
  32 +import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo;
32 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; 33 import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
33 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler; 34 import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler;
34 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue; 35 import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
35 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient; 36 import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
36 import org.thingsboard.server.transport.lwm2m.utils.TypeServer; 37 import org.thingsboard.server.transport.lwm2m.utils.TypeServer;
37 38
38 -import java.util.Map;  
39 -import java.util.UUID;  
40 import java.util.Collection; 39 import java.util.Collection;
41 import java.util.Collections; 40 import java.util.Collections;
42 import java.util.List; 41 import java.util.List;
  42 +import java.util.Map;
  43 +import java.util.UUID;
43 import java.util.concurrent.ConcurrentHashMap; 44 import java.util.concurrent.ConcurrentHashMap;
44 import java.util.concurrent.locks.Lock; 45 import java.util.concurrent.locks.Lock;
45 import java.util.concurrent.locks.ReadWriteLock; 46 import java.util.concurrent.locks.ReadWriteLock;
46 import java.util.concurrent.locks.ReentrantReadWriteLock; 47 import java.util.concurrent.locks.ReentrantReadWriteLock;
47 import java.util.stream.Collectors; 48 import java.util.stream.Collectors;
48 49
49 -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.DEFAULT_MODE;  
50 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; 50 import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
51 51
52 @Slf4j 52 @Slf4j
53 -@Component("LwM2mInMemorySecurityStore") 53 +@Service("LwM2mInMemorySecurityStore")
54 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')") 54 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
55 public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { 55 public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
56 // lock for the two maps 56 // lock for the two maps
@@ -63,26 +63,36 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { @@ -63,26 +63,36 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
63 private SecurityStoreListener listener; 63 private SecurityStoreListener listener;
64 64
65 @Autowired 65 @Autowired
66 - LwM2MGetSecurityInfo lwM2MGetSecurityInfo; 66 + LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo;
67 67
  68 + /**
  69 + * Start after DefaultAuthorizer or LwM2mPskStore
  70 + * @param endPoint -
  71 + * @return SecurityInfo
  72 + */
68 @Override 73 @Override
69 public SecurityInfo getByEndpoint(String endPoint) { 74 public SecurityInfo getByEndpoint(String endPoint) {
70 readLock.lock(); 75 readLock.lock();
71 try { 76 try {
72 String registrationId = this.getByRegistrationId(endPoint, null); 77 String registrationId = this.getByRegistrationId(endPoint, null);
73 - SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.add(endPoint); 78 + SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.addLwM2MClientToSession(endPoint);
74 return info; 79 return info;
75 } finally { 80 } finally {
76 readLock.unlock(); 81 readLock.unlock();
77 } 82 }
78 } 83 }
79 84
  85 + /**
  86 + * Start after LwM2mPskStore
  87 + * @param identity -
  88 + * @return SecurityInfo
  89 + */
80 @Override 90 @Override
81 public SecurityInfo getByIdentity(String identity) { 91 public SecurityInfo getByIdentity(String identity) {
82 readLock.lock(); 92 readLock.lock();
83 try { 93 try {
84 String integrationId = this.getByRegistrationId(null, identity); 94 String integrationId = this.getByRegistrationId(null, identity);
85 - return (integrationId != null) ? sessions.get(integrationId).getInfo() : add(identity); 95 + return (integrationId != null) ? sessions.get(integrationId).getInfo() : this.addLwM2MClientToSession(identity);
86 } finally { 96 } finally {
87 readLock.unlock(); 97 readLock.unlock();
88 } 98 }
@@ -141,11 +151,19 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { @@ -141,11 +151,19 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
141 return this.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue(); 151 return this.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
142 152
143 } 153 }
  154 +
  155 + /**
  156 + * Update in sessions (LwM2MClient for key registration_Id) after starting registration LwM2MClient in LwM2MTransportServiceImpl
  157 + * Remove from sessions LwM2MClient with key registration_Endpoint
  158 + * @param lwServer -
  159 + * @param registration -
  160 + * @return LwM2MClient after adding it to session
  161 + */
144 public LwM2MClient updateInSessionsLwM2MClient(LeshanServer lwServer, Registration registration) { 162 public LwM2MClient updateInSessionsLwM2MClient(LeshanServer lwServer, Registration registration) {
145 writeLock.lock(); 163 writeLock.lock();
146 try { 164 try {
147 if (this.sessions.get(registration.getEndpoint()) == null) { 165 if (this.sessions.get(registration.getEndpoint()) == null) {
148 - this.add(registration.getEndpoint()); 166 + this.addLwM2MClientToSession(registration.getEndpoint());
149 } 167 }
150 LwM2MClient lwM2MClient = this.sessions.get(registration.getEndpoint()); 168 LwM2MClient lwM2MClient = this.sessions.get(registration.getEndpoint());
151 lwM2MClient.setLwServer(lwServer); 169 lwM2MClient.setLwServer(lwServer);
@@ -166,45 +184,43 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { @@ -166,45 +184,43 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
166 return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null; 184 return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null;
167 } 185 }
168 186
169 - public String getByRegistrationId(String credentialsId) {  
170 - List<String> registrationIds = (this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()).size() > 0) ?  
171 - this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()) :  
172 - this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getIdentity())).map(model -> model.getKey()).collect(Collectors.toList());  
173 - return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null;  
174 - }  
175 -  
176 public Registration getByRegistration(String registrationId) { 187 public Registration getByRegistration(String registrationId) {
177 return this.sessions.get(registrationId).getRegistration(); 188 return this.sessions.get(registrationId).getRegistration();
178 } 189 }
179 190
180 - private SecurityInfo add(String identity) {  
181 - ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.CLIENT);  
182 - UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;  
183 - if (store.getSecurityInfo() != null) {  
184 - if (store.getSecurityMode() < DEFAULT_MODE.code) { 191 + /**
  192 + * Add new LwM2MClient to session
  193 + * @param identity-
  194 + * @return SecurityInfo. If error - SecurityInfoError
  195 + * and log:
  196 + * - FORBIDDEN - if there is no authorization
  197 + * - profileUuid - if the device does not have a profile
  198 + * - device - if the thingsboard does not have a device with a name equal to the identity
  199 + */
  200 + private SecurityInfo addLwM2MClientToSession(String identity) {
  201 + ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.CLIENT);
  202 + if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  203 + UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;
  204 + if (store.getSecurityInfo() != null && profileUuid != null) {
185 String endpoint = store.getSecurityInfo().getEndpoint(); 205 String endpoint = store.getSecurityInfo().getEndpoint();
186 -// sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, null, profileUuid));  
187 sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, profileUuid)); 206 sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, profileUuid));
188 - }  
189 - } else {  
190 - if (store.getSecurityMode() == NO_SEC.code && profileUuid != null)  
191 -// sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid)); 207 + } else if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) {
192 sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, profileUuid)); 208 sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, profileUuid));
193 - else {  
194 - log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity);  
195 - /**  
196 - * Return Error securityInfo  
197 - */  
198 - byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray());  
199 - SecurityInfo info = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey);  
200 - return info; 209 + } else {
  210 + log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity);
  211 + /**
  212 + * Return Error securityInfo
  213 + */
  214 + byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray());
  215 + SecurityInfo infoError = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey);
  216 + return infoError;
  217 + }
201 } 218 }
202 - }  
203 - return store.getSecurityInfo(); 219 + return store.getSecurityInfo();
204 } 220 }
205 221
206 - public Map<String, LwM2MClient> getSession (UUID sessionUuId){  
207 - return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); 222 + public Map<String, LwM2MClient> getSession(UUID sessionUuId) {
  223 + return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
208 } 224 }
209 225
210 public Map<String, LwM2MClient> getSessions() { 226 public Map<String, LwM2MClient> getSessions() {
@@ -219,13 +235,10 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { @@ -219,13 +235,10 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
219 return this.profiles.get(profileUuId); 235 return this.profiles.get(profileUuId);
220 } 236 }
221 237
222 - public Map<UUID, AttrTelemetryObserveValue>setProfiles(Map<UUID, AttrTelemetryObserveValue> profiles) { 238 + public Map<UUID, AttrTelemetryObserveValue> setProfiles(Map<UUID, AttrTelemetryObserveValue> profiles) {
223 return this.profiles = profiles; 239 return this.profiles = profiles;
224 } 240 }
225 241
226 - /**  
227 - * @param deviceProfile  
228 - */  
229 public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) { 242 public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) {
230 JsonObject profilesConfigData = LwM2MTransportHandler.getObserveAttrTelemetryFromThingsboard(deviceProfile); 243 JsonObject profilesConfigData = LwM2MTransportHandler.getObserveAttrTelemetryFromThingsboard(deviceProfile);
231 if (profilesConfigData != null) { 244 if (profilesConfigData != null) {
@@ -233,5 +246,4 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore { @@ -233,5 +246,4 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
233 } 246 }
234 return (profilesConfigData != null); 247 return (profilesConfigData != null);
235 } 248 }
236 -  
237 } 249 }
@@ -129,7 +129,6 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { @@ -129,7 +129,6 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter {
129 case FLOAT: 129 case FLOAT:
130 return String.valueOf(value); 130 return String.valueOf(value);
131 case TIME: 131 case TIME:
132 -// return Long.toString(((Date) value).getTime());  
133 String DATE_FORMAT = "MMM d, yyyy HH:mm a"; 132 String DATE_FORMAT = "MMM d, yyyy HH:mm a";
134 Long timeValue = ((Date) value).getTime(); 133 Long timeValue = ((Date) value).getTime();
135 DateFormat formatter = new SimpleDateFormat(DATE_FORMAT); 134 DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
@@ -67,11 +67,6 @@ @@ -67,11 +67,6 @@
67 <plugin> 67 <plugin>
68 <groupId>org.apache.maven.plugins</groupId> 68 <groupId>org.apache.maven.plugins</groupId>
69 <artifactId>maven-compiler-plugin</artifactId> 69 <artifactId>maven-compiler-plugin</artifactId>
70 - <version>3.1</version>  
71 - <configuration>  
72 - <source>1.8</source>  
73 - <target>1.8</target>  
74 - </configuration>  
75 </plugin> 70 </plugin>
76 <plugin> 71 <plugin>
77 <groupId>org.apache.maven.plugins</groupId> 72 <groupId>org.apache.maven.plugins</groupId>
@@ -87,4 +82,4 @@ @@ -87,4 +82,4 @@
87 </plugin> 82 </plugin>
88 </plugins> 83 </plugins>
89 </build> 84 </build>
90 -</project>  
  85 +</project>
@@ -579,7 +579,7 @@ @@ -579,7 +579,7 @@
579 <plugin> 579 <plugin>
580 <groupId>org.apache.maven.plugins</groupId> 580 <groupId>org.apache.maven.plugins</groupId>
581 <artifactId>maven-compiler-plugin</artifactId> 581 <artifactId>maven-compiler-plugin</artifactId>
582 - <version>2.5.1</version> 582 + <version>3.8.1</version>
583 <configuration> 583 <configuration>
584 <source>1.8</source> 584 <source>1.8</source>
585 <target>1.8</target> 585 <target>1.8</target>