Browse Source

Merge pull request #5525 from thingsboard/lwm2m_new_boostrap_transport

[WIP] [3.3.3] lwm2m - start refactoring
pull/5578/head
Andrew Shvayka 5 years ago
committed by GitHub
parent
commit
6584393bbf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
  2. 4
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java
  3. 10
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java
  4. 6
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java
  5. 2
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MBootstrapServersConfiguration.java
  6. 21
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java
  7. 29
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java
  8. 9
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  9. 128
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java
  10. 26
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java
  11. 255
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java
  12. 85
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java
  13. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MInMemoryBootstrapConfigStore.java
  14. 76
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  15. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java
  16. 5
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java
  17. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MClientCredentials.java
  18. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java
  19. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java
  20. 7
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  21. 5
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  22. 1
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  23. 4
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html
  24. 1
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  25. 3
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  26. 1
      ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html
  27. 3
      ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts
  28. 55
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.html
  29. 60
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.ts
  30. 39
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html
  31. 185
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts
  32. 257
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html
  33. 44
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts
  34. 90
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html
  35. 56
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
  36. 6
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts
  37. 66
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
  38. 1
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html
  39. 1
      ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html
  40. 5
      ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts
  41. 9
      ui-ngx/src/app/shared/models/device.models.ts
  42. 14
      ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts
  43. 17
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java

@ -29,8 +29,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfig;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.lwm2m.LwM2MService;
@ -61,7 +61,7 @@ public class Lwm2mController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET)
@ResponseBody
public LwM2MServerSecurityConfig getLwm2mBootstrapSecurityInfo(
public LwM2MServerSecurityConfigDefault getLwm2mBootstrapSecurityInfo(
@ApiParam(value = IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION)
@PathVariable(IS_BOOTSTRAP_SERVER) boolean bootstrapServer) throws ThingsboardException {
try {

4
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java

@ -15,10 +15,10 @@
*/
package org.thingsboard.server.service.lwm2m;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfig;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault;
public interface LwM2MService {
LwM2MServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer);
LwM2MServerSecurityConfigDefault getServerSecurityInfo(boolean bootstrapServer);
}

10
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java

@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfig;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
@ -36,14 +36,14 @@ public class LwM2MServiceImpl implements LwM2MService {
private final LwM2MTransportBootstrapConfig bootstrapConfig;
@Override
public LwM2MServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer) {
LwM2MServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig);
public LwM2MServerSecurityConfigDefault getServerSecurityInfo(boolean bootstrapServer) {
LwM2MServerSecurityConfigDefault result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig);
result.setBootstrapServerIs(bootstrapServer);
return result;
}
private LwM2MServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig) {
LwM2MServerSecurityConfig bsServ = new LwM2MServerSecurityConfig();
private LwM2MServerSecurityConfigDefault getServerSecurityConfig(LwM2MSecureServerConfig serverConfig) {
LwM2MServerSecurityConfigDefault bsServ = new LwM2MServerSecurityConfigDefault();
bsServ.setShortServerId(serverConfig.getId());
bsServ.setHost(serverConfig.getHost());
bsServ.setPort(serverConfig.getPort());

6
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java

@ -17,17 +17,19 @@ package org.thingsboard.server.common.data.device.profile;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingConfiguration;
import java.util.List;
@Data
public class Lwm2mDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
private static final long serialVersionUID = 6257277825459600068L;
private TelemetryMappingConfiguration observeAttr;
private LwM2MBootstrapServersConfiguration bootstrap;
private List<LwM2MBootstrapServerCredential> bootstrap;
private OtherConfiguration clientLwM2mSettings;
@Override

2
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MBootstrapServersConfiguration.java

@ -22,6 +22,6 @@ import java.util.List;
@Data
public class LwM2MBootstrapServersConfiguration {
List<LwM2MBootstrapServerCredential> serverConfiguration;
List<LwM2MBootstrapServerCredential> bootstrap;
}

21
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java

@ -23,21 +23,20 @@ import lombok.Data;
@Data
public class LwM2MServerSecurityConfig {
@ApiModelProperty(position = 1, value = "Server short Id. Used as link to associate server Object Instance.\nThis identifier uniquely identifies each LwM2M Server configured for the LwM2M Client.\n" +
"This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'. \n" +
@ApiModelProperty(position = 1, value = "Server short Id. Used as link to associate server Object Instance. This identifier uniquely identifies each LwM2M Server configured for the LwM2M Client. " +
"This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'. " +
"The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.", example = "123", readOnly = true)
protected Integer shortServerId = 123;
/** Security -> ObjectId = 0 'LWM2M Security' */
@ApiModelProperty(position = 2, value = "Is Bootstrap Server or Lwm2m Server", example = "true or false", readOnly = true)
@ApiModelProperty(position = 2, value = "Is Bootstrap Server or Lwm2m Server. " +
"The LwM2M Client MAY be configured to use one or more LwM2M Server Account(s). " +
"The LwM2M Client MUST have at most one LwM2M Bootstrap-Server Account. " +
"(*) The LwM2M client MUST have at least one LwM2M server account after completing the boot sequence specified.", example = "true or false", readOnly = true)
protected boolean bootstrapServerIs = false;
@ApiModelProperty(position = 3, value = "Host for 'No Security' mode", example = "0.0.0.0", readOnly = true)
protected String host;
@ApiModelProperty(position = 4, value = "Port for Lwm2m Server: 'No Security' mode: Lwm2m Server or Bootstrap Server", example = "'5685' or '5687'", readOnly = true)
protected Integer port;
@ApiModelProperty(position = 5, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", readOnly = true)
protected String securityHost;
@ApiModelProperty(position = 6, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", readOnly = true)
protected Integer securityPort;
@ApiModelProperty(position = 7, value = "Client Hold Off Time. The number of seconds to wait before initiating a Client Initiated Bootstrap once the LwM2M Client has determined it should initiate this bootstrap mode. (This information is relevant for use with a Bootstrap-Server only.)", example = "1", readOnly = true)
protected Integer clientHoldOffTime = 1;
@ApiModelProperty(position = 8, value = "Server Public Key for 'Security' mode (DTLS): RPK or X509. Format: base64 encoded", example = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAZ0pSaGKHk/GrDaUDnQZpeEdGwX7m3Ws+U/kiVat\n" +
@ -49,15 +48,15 @@ public class LwM2MServerSecurityConfig {
/** Config -> ObjectId = 1 'LwM2M Server' */
@ApiModelProperty(position = 10, value = "Specify the lifetime of the registration in seconds.", example = "300", readOnly = true)
private Integer lifetime = 300;
@ApiModelProperty(position = 11, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation.\n" +
@ApiModelProperty(position = 11, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation. " +
"If this Resource doesn’t exist, the default value is 0.", example = "1", readOnly = true)
private Integer defaultMinPeriod = 1;
/** ResourceID=6 'Notification Storing When Disabled or Offline' */
@ApiModelProperty(position = 12, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server.\n" +
"If false, the LwM2M Client discards all the “Notify” operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline.\n" +
@ApiModelProperty(position = 12, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server. " +
"If false, the LwM2M Client discards all the “Notify” operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline. " +
"The default value is true.", example = "true", readOnly = true)
private boolean notifIfDisabled = true;
@ApiModelProperty(position = 14, value = "This Resource defines the transport binding configured for the LwM2M Client.\n" +
@ApiModelProperty(position = 14, value = "This Resource defines the transport binding configured for the LwM2M Client. " +
"If the LwM2M Client supports the binding specified in this Resource, the LwM2M Client MUST use that transport for the Current Binding Mode.", example = "U", readOnly = true)
private String binding = "U";
}

29
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel
@Data
public class LwM2MServerSecurityConfigDefault extends LwM2MServerSecurityConfig {
@ApiModelProperty(position = 5, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", readOnly = true)
protected String securityHost;
@ApiModelProperty(position = 6, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", readOnly = true)
protected Integer securityPort;
}

9
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java

@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
@ -26,8 +25,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBootstrapConfigStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
@ -35,10 +34,6 @@ import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportServic
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig;

128
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java

@ -19,39 +19,27 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.thingsboard.server.common.data.device.credentials.lwm2m.AbstractLwM2MBootstrapClientCredentialWithKeys;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredential;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Data
public class LwM2MBootstrapConfig implements Serializable {
List<LwM2MBootstrapServerCredential> serversConfiguration;
LwM2MBootstrapClientCredential bootstrapClientCredential;
/*
interface BootstrapSecurityConfig
servers: BootstrapServersSecurityConfig,
bootstrapServer: ServerSecurityConfig,
lwm2mServer: ServerSecurityConfig
}
*/
/** -servers
* shortId: number,
* lifetime: number,
* defaultMinPeriod: number,
* notifIfDisabled: boolean,
* binding: string
* */
@Getter
@Setter
private LwM2MBootstrapServers servers;
List<LwM2MBootstrapServerCredential> serverConfiguration;
/** -bootstrapServer, lwm2mServer
* interface ServerSecurityConfig
@ -68,58 +56,88 @@ public class LwM2MBootstrapConfig implements Serializable {
* */
@Getter
@Setter
private LwM2MServerBootstrap bootstrapServer;
private LwM2MBootstrapClientCredential bootstrapServer;
@Getter
@Setter
private LwM2MServerBootstrap lwm2mServer;
private LwM2MBootstrapClientCredential lwm2mServer;
public LwM2MBootstrapConfig(){};
public LwM2MBootstrapConfig(List<LwM2MBootstrapServerCredential> serverConfiguration, LwM2MBootstrapClientCredential bootstrapClientServer, LwM2MBootstrapClientCredential lwm2mClientServer) {
this.serverConfiguration = serverConfiguration;
this.bootstrapServer = bootstrapClientServer;
this.lwm2mServer = lwm2mClientServer;
public LwM2MBootstrapConfig(List<LwM2MBootstrapServerCredential> serversConfiguration, LwM2MBootstrapClientCredential bootstrapClientCredential) {
this.serversConfiguration = serversConfiguration;
this.bootstrapClientCredential = bootstrapClientCredential;
}
@JsonIgnore
public BootstrapConfig getLwM2MBootstrapConfig() {
BootstrapConfig configBs = new BootstrapConfig();
/* Delete old security objects */
configBs.toDelete.add("/0");
configBs.toDelete.add("/1");
/* Server Configuration (object 1) as defined in LWM2M 1.0.x TS. */
BootstrapConfig.ServerConfig server0 = new BootstrapConfig.ServerConfig();
server0.shortId = servers.getShortId();
server0.lifetime = servers.getLifetime();
server0.defaultMinPeriod = servers.getDefaultMinPeriod();
server0.notifIfDisabled = servers.isNotifIfDisabled();
server0.binding = BindingMode.parse(servers.getBinding());
configBs.servers.put(0, server0);
/* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */
this.bootstrapServer.setBootstrapServerIs(true);
configBs.security.put(0, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.bootstrapServer.isBootstrapServerIs(), this.bootstrapServer.getSecurityMode(), this.bootstrapServer.getClientPublicKeyOrId(), this.bootstrapServer.getServerPublicKey(), this.bootstrapServer.getClientSecretKey(), this.bootstrapServer.getServerId()));
/* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Server instance = 1 */
configBs.security.put(1, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.getSecurityHost(), this.lwm2mServer.getSecurityPort(), this.lwm2mServer.isBootstrapServerIs(), this.lwm2mServer.getSecurityMode(), this.lwm2mServer.getClientPublicKeyOrId(), this.lwm2mServer.getServerPublicKey(), this.lwm2mServer.getClientSecretKey(), this.lwm2mServer.getServerId()));
configBs.autoIdForSecurityObject = true;
int id = 0;
for (LwM2MBootstrapServerCredential serverCredential : serverConfiguration) {
BootstrapConfig.ServerConfig serverConfig = setServerConfig((AbstractLwM2MBootstrapServerCredential) serverCredential);
configBs.servers.put(id, serverConfig);
BootstrapConfig.ServerSecurity serverSecurity = setServerSecurity((AbstractLwM2MBootstrapServerCredential) serverCredential, serverCredential.getSecurityMode());
configBs.security.put(id, serverSecurity);
id++;
}
/** in LwM2mDefaultBootstrapSessionManager -> initTasks
* Delete all security/config objects if update bootstrap server and lwm2m server
* if other: del or update only instances */
return configBs;
}
private BootstrapConfig.ServerSecurity setServerSecurity(String host, Integer port, String securityHost, Integer securityPort, boolean bootstrapServer, SecurityMode securityMode, String clientPublicKey, String serverPublicKey, String secretKey, int serverId) {
private BootstrapConfig.ServerSecurity setServerSecurity(AbstractLwM2MBootstrapServerCredential serverCredential, LwM2MSecurityMode securityMode) {
BootstrapConfig.ServerSecurity serverSecurity = new BootstrapConfig.ServerSecurity();
if (securityMode.equals(SecurityMode.NO_SEC)) {
serverSecurity.uri = "coap://" + host + ":" + Integer.toString(port);
} else {
serverSecurity.uri = "coaps://" + securityHost + ":" + Integer.toString(securityPort);
String serverUri = "coap://";
byte[] publicKeyOrId = new byte[]{};
byte[] secretKey = new byte[]{};
byte[] serverPublicKey = new byte[]{};
serverSecurity.serverId = serverCredential.getShortServerId();
log.info("serverId = [{}]", serverSecurity.serverId);
serverSecurity.securityMode = SecurityMode.valueOf(securityMode.name());
serverSecurity.bootstrapServer = serverCredential.isBootstrapServerIs();
if (!LwM2MSecurityMode.NO_SEC.equals(securityMode)) {
AbstractLwM2MBootstrapClientCredentialWithKeys server;
if (serverSecurity.bootstrapServer) {
server = (AbstractLwM2MBootstrapClientCredentialWithKeys) this.bootstrapServer;
} else {
server = (AbstractLwM2MBootstrapClientCredentialWithKeys) this.lwm2mServer;
}
serverUri = "coaps://";
if (LwM2MSecurityMode.PSK.equals(securityMode)) {
publicKeyOrId = server.getClientPublicKeyOrId().getBytes();
secretKey = Hex.decodeHex(server.getClientSecretKey().toCharArray());
log.info("publicKeyOrId [{}]", new String(publicKeyOrId, StandardCharsets.UTF_8));
} else {
publicKeyOrId = server.getDecodedClientPublicKeyOrId();
secretKey = server.getDecodedClientSecretKey();
log.info("publicKeyOrId [{}]", Hex.encodeHexString(publicKeyOrId));
}
serverPublicKey = serverCredential.getDecodedCServerPublicKey();
}
serverSecurity.bootstrapServer = bootstrapServer;
serverSecurity.securityMode = securityMode;
serverSecurity.publicKeyOrId = setPublicKeyOrId(clientPublicKey, securityMode);
serverSecurity.serverPublicKey = (serverPublicKey != null && !serverPublicKey.isEmpty()) ? Hex.decodeHex(serverPublicKey.toCharArray()) : new byte[]{};
serverSecurity.secretKey = (secretKey != null && !secretKey.isEmpty()) ? Hex.decodeHex(secretKey.toCharArray()) : new byte[]{};
serverSecurity.serverId = serverId;
log.info("secretKey [{}]", Hex.encodeHexString(secretKey));
serverUri += (((serverCredential.getHost().equals("0.0.0.0") ? "localhost" : serverCredential.getHost()) + ":" + serverCredential.getPort()));
log.info("serverSecurity.uri = [{}]", serverUri);
serverSecurity.uri = serverUri;
serverSecurity.publicKeyOrId = publicKeyOrId;
serverSecurity.secretKey = secretKey;
serverSecurity.serverPublicKey = serverPublicKey;
log.info("server [{}]", Hex.encodeHexString(serverSecurity.serverPublicKey));
return serverSecurity;
}
private byte[] setPublicKeyOrId(String publicKeyOrIdStr, SecurityMode securityMode) {
return (publicKeyOrIdStr == null || publicKeyOrIdStr.isEmpty()) ? new byte[]{} :
SecurityMode.PSK.equals(securityMode) ? publicKeyOrIdStr.getBytes(StandardCharsets.UTF_8) :
Hex.decodeHex(publicKeyOrIdStr.toCharArray());
private BootstrapConfig.ServerConfig setServerConfig (AbstractLwM2MBootstrapServerCredential serverCredential) {
BootstrapConfig.ServerConfig serverConfig = new BootstrapConfig.ServerConfig();
serverConfig.shortId = serverCredential.getShortServerId();
serverConfig.lifetime = serverCredential.getLifetime();
serverConfig.defaultMinPeriod = serverCredential.getDefaultMinPeriod();
serverConfig.notifIfDisabled = serverCredential.isNotifIfDisabled();
serverConfig.binding = BindingMode.parse(serverCredential.getBinding());
return serverConfig;
}
}

26
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java

@ -16,13 +16,14 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.BootstrapDiscoverRequest;
import org.eclipse.leshan.core.request.BootstrapDownlinkRequest;
import org.eclipse.leshan.core.request.BootstrapFinishRequest;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.response.BootstrapDiscoverResponse;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.BootstrapConfigStoreTaskProvider;
import org.eclipse.leshan.server.bootstrap.BootstrapFailureCause;
import org.eclipse.leshan.server.bootstrap.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.BootstrapTaskProvider;
@ -34,12 +35,13 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore;
import org.eclipse.leshan.server.security.SecurityChecker;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapConfigStoreTaskProvider;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_INFO;
@Slf4j
@ -58,7 +60,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
* @param bsSecurityStore the {@link BootstrapSecurityStore} used by default {@link SecurityChecker}.
*/
public LwM2mDefaultBootstrapSessionManager(BootstrapSecurityStore bsSecurityStore, BootstrapConfigStore configStore, TransportService transportService) {
this(bsSecurityStore, new SecurityChecker(), new BootstrapConfigStoreTaskProvider(configStore),
this(bsSecurityStore, new SecurityChecker(), new LwM2MBootstrapConfigStoreTaskProvider(configStore),
new StandardBootstrapModelProvider());
this.transportService = transportService;
}
@ -118,6 +120,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
session.setModel(modelProvider.getObjectModel(session, tasks.supportedObjects));
// set Requests to Send
log.info("tasks.requestsToSend = [{}]", tasks.requestsToSend);
session.setRequests(tasks.requestsToSend);
// prepare list where we will store Responses
@ -162,14 +165,15 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
// store response
DefaultBootstrapSession session = (DefaultBootstrapSession) bsSession;
session.getResponses().add(response);
this.sendLogs (bsSession.getEndpoint(),
String.format("%s: %s %s receives success response %s for %s : %s", LOG_LWM2M_INFO,
request.getClass().getSimpleName(), request.getPath().toString(), response.toString(), bsSession.toString(), request.toString()));
String msg = String.format("%s: %s %s receives success response %s for %s : %s", LOG_LWM2M_INFO,
request.getClass().getSimpleName(), request.getPath().toString(), response.toString(), bsSession.toString(), request.toString());
this.sendLogs(bsSession.getEndpoint(), msg);
// on success for NOT bootstrap finish request we send next request
return BootstrapPolicy.continueWith(nextRequest(bsSession));
} else {
// on success for bootstrap finish request we stop the session
this.sendLogs (bsSession.getEndpoint(),
this.sendLogs(bsSession.getEndpoint(),
String.format("%s: %s receives success response for bootstrap finish request and stop the session: %s", LOG_LWM2M_INFO,
request.getClass().getSimpleName(), bsSession.toString()));
return BootstrapPolicy.finished();
@ -192,9 +196,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
} else {
// on response error for bootstrap finish request we stop the session
this.sendLogs (bsSession.getEndpoint(),
String.format("%s: %s %s error response %s for request %s bootstrap finish. Stop the session: %s", LOG_LWM2M_INFO,
request.getClass().getSimpleName(),
request.getPath().toString(), response.toString(), request.toString(), bsSession.toString()));
String.format("%s: error response for request bootstrap finish. Stop the session: %s", LOG_LWM2M_ERROR, bsSession.toString()));
return BootstrapPolicy.failed();
}
}

255
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapConfigStoreTaskProvider.java

@ -0,0 +1,255 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.bootstrap.store;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.Link;
import org.eclipse.leshan.core.node.LwM2mObject;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.request.*;
import org.eclipse.leshan.core.response.BootstrapDiscoverResponse;
import org.eclipse.leshan.core.response.BootstrapReadResponse;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.BootstrapTaskProvider;
import org.eclipse.leshan.server.bootstrap.BootstrapUtil;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.stream.Collectors;
import static org.eclipse.leshan.server.bootstrap.BootstrapUtil.toWriteRequest;
@Slf4j
public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvider {
private BootstrapConfigStore store;
private Map<Integer, String> supportedObjects;
/**
* Map<serverId, InstanceId>
*/
protected Map<Integer, Integer> securityInstances;
protected Map<Integer, Integer> serverInstances;
protected Integer bootstrapServerIdOld;
protected Integer bootstrapServerIdNew;
public LwM2MBootstrapConfigStoreTaskProvider(BootstrapConfigStore store) {
this.store = store;
}
@Override
public Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResponse) {
BootstrapConfig config = store.get(session.getEndpoint(), session.getIdentity(), session);
if (config == null) {
return null;
}
if (previousResponse == null && shouldStartWithDiscover(config)) {
Tasks tasks = new Tasks();
tasks.requestsToSend = new ArrayList<>(1);
tasks.requestsToSend.add(new BootstrapDiscoverRequest());
tasks.last = false;
return tasks;
} else {
Tasks tasks = new Tasks();
if (this.supportedObjects == null) {
initSupportedObjectsDefault();
}
// add supportedObjects
tasks.supportedObjects = this.supportedObjects;
// handle bootstrap discover response
if (previousResponse != null) {
if (previousResponse.get(0) instanceof BootstrapDiscoverResponse) {
BootstrapDiscoverResponse discoverResponse = (BootstrapDiscoverResponse) previousResponse.get(0);
if (discoverResponse.isSuccess()) {
this.initAfterBootstrapDiscover(discoverResponse);
findSecurityInstanceId(discoverResponse.getObjectLinks());
} else {
log.info(
"Bootstrap Discover return error {} : to continue bootstrap session without autoIdForSecurityObject mode. {}",
discoverResponse, session);
}
if (this.securityInstances.get(0) == null) {
log.error(
"Unable to find bootstrap server instance in Security Object (0) in response {}: unable to continue bootstrap session with autoIdForSecurityObject mode. {}",
discoverResponse, session);
return null;
}
tasks.requestsToSend = new ArrayList<>(1);
tasks.requestsToSend.add(new BootstrapReadRequest("/1"));
tasks.last = false;
return tasks;
}
BootstrapReadResponse readResponse = (BootstrapReadResponse) previousResponse.get(0);
findServerInstanceId(readResponse);
// create requests from config
tasks.requestsToSend = this.toRequests(config,
config.contentFormat != null ? config.contentFormat : session.getContentFormat());
} else {
// create requests from config
tasks.requestsToSend = BootstrapUtil.toRequests(config,
config.contentFormat != null ? config.contentFormat : session.getContentFormat());
}
return tasks;
}
}
protected boolean shouldStartWithDiscover(BootstrapConfig config) {
return config.autoIdForSecurityObject;
}
protected void findSecurityInstanceId(Link[] objectLinks) {
log.info("Object after discover: [{}]", objectLinks);
this.securityInstances = new HashMap<>();
for (Link link : objectLinks) {
if (link.getUrl().startsWith("/0/")) {
try {
LwM2mPath path = new LwM2mPath(link.getUrl());
if (path.isObjectInstance()) {
if (link.getAttributes().containsKey("ssid")) {
int serverId = Integer.valueOf(link.getAttributes().get("ssid"));
if (!this.securityInstances.containsKey(serverId)) {
this.securityInstances.put(serverId, path.getObjectInstanceId());
} else {
log.error(String.format("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId()));
}
this.securityInstances.put(Integer.valueOf(link.getAttributes().get("ssid")), path.getObjectInstanceId());
} else {
if (!this.securityInstances.containsKey(0)) {
this.securityInstances.put(0, path.getObjectInstanceId());
} else {
log.error(String.format("Invalid bootstrapSecurityInstance by [{}]", path.getObjectInstanceId()));
}
}
}
} catch (Exception e) {
// ignore if this is not a LWM2M path
log.error("Invalid LwM2MPath starting by \"/0/\"");
}
}
}
}
protected void findServerInstanceId(BootstrapReadResponse readResponse) {
this.serverInstances = new HashMap<>();
((LwM2mObject) readResponse.getContent()).getInstances().values().forEach(instance -> {
serverInstances.put(((Long) instance.getResource(0).getValue()).intValue(), instance.getId());
});
if (this.securityInstances != null && this.securityInstances.size() > 0 && this.serverInstances != null && this.serverInstances.size() > 0) {
this.findBootstrapServerId();
}
}
protected void findBootstrapServerId() {
Map<Integer, Integer> filteredMap = this.serverInstances.entrySet()
.stream().filter(x -> !this.securityInstances.containsKey(x.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
this.bootstrapServerIdOld = filteredMap.keySet().stream().findFirst().get();
}
public BootstrapConfigStore getStore() {
return this.store;
}
private void initAfterBootstrapDiscover(BootstrapDiscoverResponse response) {
Link[] links = response.getObjectLinks();
Arrays.stream(links).forEach(link -> {
LwM2mPath path = new LwM2mPath(link.getUrl());
if (path != null && !path.isRoot() && path.getObjectId() < 3) {
if (path.isObject()) {
String ver = link.getAttributes().get("ver") != null ? link.getAttributes().get("ver") : "1.0";
this.supportedObjects.put(path.getObjectId(), ver);
}
}
});
}
public List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(BootstrapConfig bootstrapConfig,
ContentFormat contentFormat) {
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests = new ArrayList<>();
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestsDelete = new ArrayList<>();
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestsWrite = new ArrayList<>();
boolean isBsServer = false;
boolean isLwServer = false;
/** Map<serverId, InstanceId> */
Map<Integer, Integer> instances = new HashMap<>();
// handle security
int id = 0;
for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfig.security).values()) {
if (security.bootstrapServer) {
requestsWrite.add(toWriteRequest(this.securityInstances.get(0), security, contentFormat));
isBsServer = true;
this.bootstrapServerIdNew = security.serverId;
instances.put(security.serverId, this.securityInstances.get(0));
} else {
if (id == this.securityInstances.get(0)) {
id++;
}
requestsWrite.add(toWriteRequest(id, security, contentFormat));
instances.put(security.serverId, id);
isLwServer = true;
if (!isBsServer && this.securityInstances.containsKey(security.serverId) && id != this.securityInstances.get(security.serverId)) {
requestsDelete.add(new BootstrapDeleteRequest("/0/" + this.securityInstances.get(security.serverId)));
}
id++;
}
}
// handle server
for (Map.Entry<Integer, BootstrapConfig.ServerConfig> server : bootstrapConfig.servers.entrySet()) {
int securityInstanceId = instances.get(server.getValue().shortId);
requestsWrite.add(toWriteRequest(securityInstanceId, server.getValue(), contentFormat));
if (!isLwServer && this.bootstrapServerIdNew != null && server.getValue().shortId == this.bootstrapServerIdNew &&
(this.bootstrapServerIdNew != this.bootstrapServerIdOld || securityInstanceId != this.serverInstances.get(this.bootstrapServerIdOld))) {
requestsDelete.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(this.bootstrapServerIdOld)));
} else {
if (!isBsServer && this.serverInstances.containsKey(server.getValue().shortId) && securityInstanceId != this.serverInstances.get(server.getValue().shortId)) {
requestsDelete.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(server.getValue().shortId)));
}
}
}
// handle acl
for (Map.Entry<Integer, BootstrapConfig.ACLConfig> acl : bootstrapConfig.acls.entrySet()) {
requestsWrite.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat));
}
// handle delete
if (isBsServer & isLwServer) {
requests.add(new BootstrapDeleteRequest("/0"));
requests.add(new BootstrapDeleteRequest("/1"));
} else if (requestsDelete.size() > 0) {
requests.addAll(requestsDelete);
}
// handle write
if (requestsWrite.size() > 0) {
requests.addAll(requestsWrite);
}
return (requests);
}
private void initSupportedObjectsDefault() {
this.supportedObjects = new HashMap<>();
this.supportedObjects.put(0, "1.1");
this.supportedObjects.put(1, "1.1");
this.supportedObjects.put(2, "1.0");
}
}

85
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MBootstrapSecurityStore.java

@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
package org.thingsboard.server.transport.lwm2m.bootstrap.store;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import org.eclipse.leshan.server.bootstrap.EditableBootstrapConfigStore;
import org.eclipse.leshan.server.bootstrap.InvalidConfigurationException;
@ -26,9 +24,10 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
@ -42,12 +41,12 @@ import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_INFO;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.LOG_LWM2M_TELEMETRY;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.getBootstrapParametersFromThingsboard;
@Slf4j
@Service("LwM2MBootstrapSecurityStore")
@ -71,10 +70,12 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override
public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) {
// TODO
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
if (store.getBootstrapCredentialConfig() != null) {
/* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
endPoint = store.getEndpoint();
BootstrapConfig bsConfigNew = store.getBootstrapConfig();
if (bsConfigNew != null) {
try {
@ -121,34 +122,6 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
/* BootstrapConfig */
LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store);
if (lwM2MBootstrapConfig != null) {
/* Security info */
switch (lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode()) {
/* Use RPK only */
case PSK:
store.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(store.getEndpoint(),
lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId(),
Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientSecretKey().toCharArray())));
store.setSecurityMode(SecurityMode.PSK);
break;
case RPK:
try {
store.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(store.getEndpoint(),
SecurityUtil.publicKey.decode(Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId().toCharArray()))));
store.setSecurityMode(SecurityMode.RPK);
break;
} catch (IOException | GeneralSecurityException e) {
log.error("Unable to decode Client public key for [{}] [{}]", store.getEndpoint(), e.getMessage());
}
case X509:
store.setSecurityInfo(SecurityInfo.newX509CertInfo(store.getEndpoint()));
store.setSecurityMode(SecurityMode.X509);
break;
case NO_SEC:
store.setSecurityMode(SecurityMode.NO_SEC);
store.setSecurityInfo(null);
break;
default:
}
BootstrapConfig bootstrapConfig = lwM2MBootstrapConfig.getLwM2MBootstrapConfig();
store.setBootstrapConfig(bootstrapConfig);
}
@ -157,25 +130,11 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) {
LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig();
if (lwM2MBootstrapConfig != null) {
LwM2MBootstrapServersConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
lwM2MBootstrapConfig.setServers(JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class));
LwM2MServerBootstrap bootstrapServerProfile = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class);
if (SecurityMode.NO_SEC != bootstrapServerProfile.getSecurityMode() && bootstrapServerProfile != null) {
bootstrapServerProfile.setSecurityHost(bootstrapServerProfile.getHost());
bootstrapServerProfile.setSecurityPort(bootstrapServerProfile.getPort());
}
LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class);
if (SecurityMode.NO_SEC != profileLwm2mServer.getSecurityMode() && profileLwm2mServer != null) {
profileLwm2mServer.setSecurityHost(profileLwm2mServer.getHost());
profileLwm2mServer.setSecurityPort(profileLwm2mServer.getPort());
}
UUID sessionUUiD = UUID.randomUUID();
TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
bsSessions.put(store.getEndpoint(), sessionInfo);
context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo, context.getTransportService()));
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile, lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer)) {
lwM2MBootstrapConfig.setBootstrapServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile));
lwM2MBootstrapConfig.setLwm2mServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer));
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig)) {
String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint());
helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo);
return lwM2MBootstrapConfig;
@ -195,15 +154,27 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
* Bootstrap security have to sync between (bootstrapServer in credential and bootstrapServer in profile)
* and (lwm2mServer in credential and lwm2mServer in profile
*
* @param bootstrapFromCredential - Bootstrap -> Security of bootstrapServer in credential
* @param bootstrapServerProfile - Bootstrap -> Security of bootstrapServer in profile
* @param lwm2mFromCredential - Bootstrap -> Security of lwm2mServer in credential
* @param profileLwm2mServer - Bootstrap -> Security of lwm2mServer in profile
* @return false if not sync between SecurityMode of Bootstrap credential and profile
*/
private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap bootstrapServerProfile, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
return (bootstrapFromCredential.getSecurityMode().equals(bootstrapServerProfile.getSecurityMode()) &&
lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode()));
// private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap bootstrapServerProfile, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
private boolean getValidatedSecurityMode(LwM2MBootstrapConfig lwM2MBootstrapConfig) {
LwM2MSecurityMode bootstrapServerSecurityMode = lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode();
LwM2MSecurityMode lwm2mServerSecurityMode = lwM2MBootstrapConfig.getLwm2mServer().getSecurityMode();
AtomicBoolean validBs = new AtomicBoolean(true);
AtomicBoolean validLw = new AtomicBoolean(true);
lwM2MBootstrapConfig.getServerConfiguration().forEach(serverCredential -> {
if (((AbstractLwM2MBootstrapServerCredential)serverCredential).isBootstrapServerIs()) {
if (!bootstrapServerSecurityMode.equals(serverCredential.getSecurityMode())) {
validBs.set(false);
}
}
else {
if (!lwm2mServerSecurityMode.equals(serverCredential.getSecurityMode())) {
validLw.set(false);
}
}
});
return validBs.get()&validLw.get();
}
public TransportProtos.SessionInfoProto getSessionByEndpoint(String endpoint) {

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MInMemoryBootstrapConfigStore.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/store/LwM2MInMemoryBootstrapConfigStore.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
package org.thingsboard.server.transport.lwm2m.bootstrap.store;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.Identity;

76
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java

@ -24,15 +24,16 @@ import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredential;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredential;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MClientCredentials;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer;
@ -48,6 +49,7 @@ import static org.eclipse.leshan.core.SecurityMode.PSK;
import static org.eclipse.leshan.core.SecurityMode.RPK;
import static org.eclipse.leshan.core.SecurityMode.X509;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT;
@Slf4j
@Component
@ -67,17 +69,16 @@ public class LwM2mCredentialsSecurityInfoValidator {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
log.trace("Validated credentials: [{}] [{}]", credentialsId, msg);
String credentialsBody = msg.getCredentials();
resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue);
resultSecurityStore[0].setMsg(msg);
resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile());
resultSecurityStore[0] = createSecurityInfo(credentialsId, msg, keyValue);
latch.countDown();
}
@Override
public void onError(Throwable e) {
log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e);
resultSecurityStore[0] = createSecurityInfo(credentialsId, null, null);
TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
result.setEndpoint(credentialsId);
resultSecurityStore[0] = result;
latch.countDown();
}
});
@ -88,51 +89,46 @@ public class LwM2mCredentialsSecurityInfoValidator {
}
TbLwM2MSecurityInfo securityInfo = resultSecurityStore[0];
if (securityInfo.getSecurityMode() == null) {
if ((CLIENT.equals(keyValue) && securityInfo.getSecurityMode() == null)) {
throw new LwM2MAuthException();
}
return securityInfo;
}
/**
* Create new SecurityInfo
* @param endpoint -
* @param jsonStr -
* @param keyValue -
*
* @return SecurityInfo
*/
private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTypeServer keyValue) {
private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, ValidateDeviceCredentialsResponse msg, LwM2mTypeServer keyValue) {
TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class);
LwM2MClientCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MClientCredentials.class);
if (credentials != null) {
result.setMsg(msg);
result.setDeviceProfile(msg.getDeviceProfile());
result.setEndpoint(credentials.getClient().getEndpoint());
// if ((keyValue.equals(CLIENT))) {
switch (credentials.getClient().getSecurityConfigClientMode()) {
case NO_SEC:
createClientSecurityInfoNoSec(result);
break;
case PSK:
createClientSecurityInfoPSK(result, endpoint, credentials.getClient());
break;
case RPK:
createClientSecurityInfoRPK(result, endpoint, credentials.getClient());
break;
case X509:
createClientSecurityInfoX509(result, endpoint, credentials.getClient());
break;
default:
break;
}
// } else
if (keyValue.equals(BOOTSTRAP)) {
result.setBootstrapCredentialConfig(credentials.getBootstrap());
if (LwM2MSecurityMode.PSK.equals(credentials.getClient().getSecurityConfigClientMode())) {
PSKClientCredential pskClientConfig = (PSKClientCredential) credentials.getClient();
endpoint = StringUtils.isNotEmpty(pskClientConfig.getEndpoint()) ? pskClientConfig.getEndpoint() : endpoint;
}
result.setEndpoint(endpoint);
result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode());
} else {
result.setEndpoint(credentials.getClient().getEndpoint());
switch (credentials.getClient().getSecurityConfigClientMode()) {
case NO_SEC:
createClientSecurityInfoNoSec(result);
break;
case PSK:
createClientSecurityInfoPSK(result, endpoint, credentials.getClient());
break;
case RPK:
createClientSecurityInfoRPK(result, endpoint, credentials.getClient());
break;
case X509:
createClientSecurityInfoX509(result, endpoint, credentials.getClient());
break;
default:
break;
}
LwM2MBootstrapConfig bootstrapCredentialConfig = new LwM2MBootstrapConfig(((Lwm2mDeviceProfileTransportConfiguration) msg.getDeviceProfile().getProfileData().getTransportConfiguration()).getBootstrap(),
credentials.getBootstrap().getBootstrapServer(), credentials.getBootstrap().getLwm2mServer());
result.setBootstrapCredentialConfig(bootstrapCredentialConfig);
}
}
return result;

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java

@ -41,7 +41,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MClientCredentials;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
@ -124,7 +124,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
}
ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
LwM2MClientCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MClientCredentials.class);
if (!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)) {
continue;
}

5
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java

@ -28,12 +28,13 @@ import java.io.Serializable;
@Data
public class TbLwM2MSecurityInfo implements Serializable {
private ValidateDeviceCredentialsResponse msg;
private DeviceProfile deviceProfile;
private String endpoint;
private SecurityInfo securityInfo;
private SecurityMode securityMode;
/** bootstrap */
private DeviceProfile deviceProfile;
private LwM2MBootstrapConfig bootstrapCredentialConfig;
private String endpoint;
private BootstrapConfig bootstrapConfig;
}

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MCredentials.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/credentials/LwM2MClientCredentials.java

@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCr
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
@Data
public class LwM2MCredentials {
public class LwM2MClientCredentials {
private LwM2MClientCredential client;
private LwM2MBootstrapConfig bootstrap;
}

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java

@ -22,6 +22,7 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.jetbrains.annotations.Nullable;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import java.util.Arrays;
import java.util.HashSet;
@ -71,7 +72,12 @@ public class TbLwM2mSecurityStore implements TbMainSecurityStore {
public SecurityInfo getByIdentity(String pskIdentity) {
SecurityInfo securityInfo = securityStore.getByIdentity(pskIdentity);
if (securityInfo == null) {
securityInfo = fetchAndPutSecurityInfo(pskIdentity);
try {
securityInfo = fetchAndPutSecurityInfo(pskIdentity);
} catch (LwM2MAuthException e) {
log.info("Registration failed: FORBIDDEN, endpointId: [{}]", pskIdentity);
securityInfo = SecurityInfo.newPreSharedKeyInfo(pskIdentity, pskIdentity, new byte[]{0x00});
}
}
return securityInfo;
}

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java

@ -39,7 +39,7 @@ import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.ota.OtaPackageKey;
@ -181,7 +181,7 @@ public class LwM2MTransportUtil {
}
}
public static LwM2MBootstrapServersConfiguration getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) {
public static List<LwM2MBootstrapServerCredential> getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) {
return toLwM2MClientProfile(deviceProfile).getBootstrap();
}

7
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java

@ -48,6 +48,9 @@ import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
@ -238,7 +241,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
case PSK:
PSKClientCredential pskCredentials = (PSKClientCredential) clientCredentials;
if (StringUtils.isBlank(pskCredentials.getIdentity())) {
throw new DeviceCredentialsValidationException("LwM2M client PSK identity must be specified!");
throw new DeviceCredentialsValidationException("LwM2M client PSK identity must be specified and must be an utf8 string!");
}
// SecurityMode.NO_SEC.toString() == "NO_SEC";
if (pskCredentials.getIdentity().equals(SecurityMode.NO_SEC.toString())) {
@ -295,7 +298,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
case PSK:
PSKBootstrapClientCredential pskCredentials = (PSKBootstrapClientCredential) serverCredentials;
if (StringUtils.isBlank(pskCredentials.getClientPublicKeyOrId())) {
throw new DeviceCredentialsValidationException(server + " client PSK public key or id must be specified!");
throw new DeviceCredentialsValidationException(server + " client PSK public key or id must be specified and must be an utf8 string!");
}
// SecurityMode.NO_SEC.toString() == "NO_SEC";

5
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -46,7 +46,6 @@ import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServersConfiguration;
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration;
import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration;
@ -418,8 +417,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
}
}
} else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) {
LwM2MBootstrapServersConfiguration lwM2MBootstrapServersConfiguration = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap();
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfiguration.getServerConfiguration()) {
List<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap();
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) {
validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential);
}
}

1
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -100,6 +100,7 @@
</mat-form-field>
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
isAdd="true"
required>
</tb-device-profile-transport-configuration>
</form>

4
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html

@ -34,9 +34,7 @@
<div *ngIf="!disabled" style="padding-top: 16px;">
<button mat-raised-button color="primary"
type="button"
(click)="addAlarm()"
matTooltip="{{ 'device-profile.add-alarm-rule' | translate }}"
matTooltipPosition="above">
(click)="addAlarm()">
<span translate>device-profile.add-alarm-rule</span>
</button>
</div>

1
ui-ngx/src/app/modules/home/components/profile/device-profile.component.html

@ -138,6 +138,7 @@
<ng-template matExpansionPanelContent>
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
[isAdd] = "isTransportTypeChanged"
required>
</tb-device-profile-transport-configuration>
</ng-template>

3
ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts

@ -67,6 +67,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
displayTransportConfiguration: boolean;
isTransportTypeChanged = false;
serviceType = ServiceType.TB_RULE_ENGINE;
deviceProfileId: EntityId;
@ -158,6 +160,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
const deviceTransportType: DeviceTransportType = form.get('transportType').value;
this.displayTransportConfiguration = deviceTransportType &&
deviceTransportTypeConfigurationInfoMap.get(deviceTransportType).hasProfileConfiguration;
this.isTransportTypeChanged = true;
let profileData: DeviceProfileData = form.getRawValue().profileData;
if (!profileData) {
profileData = {

1
ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html

@ -37,6 +37,7 @@
</ng-template>
<ng-template [ngSwitchCase]="deviceTransportType.LWM2M">
<tb-profile-lwm2m-device-transport-configuration
[isAdd]="isAdd"
[required]="required"
formControlName="configuration">
</tb-profile-lwm2m-device-transport-configuration>

3
ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts

@ -50,6 +50,9 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu
@Input()
disabled: boolean;
@Input()
isAdd: boolean;
transportType: DeviceTransportType;
private propagateChange = (v: any) => { };

55
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.html

@ -0,0 +1,55 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<form [formGroup]="addConfigServerFormGroup" style="width: 400px;">
<mat-toolbar fxLayout="row" color="primary">
<h2 translate>device-profile.lwm2m.add-new-server-title</h2>
<span fxFlex></span>
<button mat-button mat-icon-button (click)="cancel()" type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content fxLayout="column">
<mat-form-field>
<mat-label>{{ 'device-profile.lwm2m.server-type' | translate }}</mat-label>
<mat-select formControlName="serverType">
<mat-option *ngFor="let serverType of serverTypes"
[value]="serverType">
{{ serverConfigTypeNamesMap.get(serverType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div mat-dialog-actions fxLayout="row">
<span fxFlex></span>
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
type="button"
[disabled]="(isLoading$ | async) || addConfigServerFormGroup.invalid"
(click)="addServerConfig()">
{{ 'action.add' | translate }}
</button>
</div>
</form>

60
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component.ts

@ -0,0 +1,60 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MatDialogRef } from '@angular/material/dialog';
import {
ServerConfigType,
ServerConfigTypeTranslationMap
} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
@Component({
selector: 'tb-profile-lwm2m-bootstrap-add-config-server-dialog',
templateUrl: './lwm2m-bootstrap-add-config-server-dialog.component.html'
})
export class Lwm2mBootstrapAddConfigServerDialogComponent extends DialogComponent<Lwm2mBootstrapAddConfigServerDialogComponent> {
addConfigServerFormGroup: FormGroup;
serverTypes = Object.values(ServerConfigType);
serverConfigTypeNamesMap = ServerConfigTypeTranslationMap;
constructor(protected store: Store<AppState>,
protected router: Router,
private fb: FormBuilder,
public dialogRef: MatDialogRef<Lwm2mBootstrapAddConfigServerDialogComponent, boolean>,
) {
super(store, router, dialogRef);
this.addConfigServerFormGroup = this.fb.group({
serverType: [ServerConfigType.LWM2M]
});
}
addServerConfig() {
this.dialogRef.close(this.addConfigServerFormGroup.get('serverType').value === ServerConfigType.BOOTSTRAP);
}
cancel(): void {
this.dialogRef.close(null);
}
}

39
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html

@ -0,0 +1,39 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div fxLayout="column">
<mat-accordion multi="true">
<div *ngFor="let serverConfig of serverConfigsFromArray().controls; trackBy: trackByParams; let $index = index;">
<tb-profile-lwm2m-device-config-server
[formControl]="serverConfig"
(removeServer)="removeServerConfig($event, $index)">
</tb-profile-lwm2m-device-config-server>
</div>
</mat-accordion>
<div *ngIf="!serverConfigsFromArray().controls.length" style="margin:32px 0">
<span translate fxLayoutAlign="center center"
class="tb-prompt">device-profile.lwm2m.no-config-servers</span>
</div>
<div *ngIf="!disabled" style="padding-top: 16px;">
<button mat-raised-button color="primary"
type="button"
(click)="addServerConfig()">
<span>{{ (isBootstrapAdded() ? 'device-profile.lwm2m.add-lwm2m-server-config' :
'device-profile.lwm2m.add-server-config') | translate}}</span>
</button>
</div>
</div>

185
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts

@ -0,0 +1,185 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormArray,
FormBuilder, FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR
} from '@angular/forms';
import { of, Subscription } from 'rxjs';
import { ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
import { TranslateService } from '@ngx-translate/core';
import { DialogService } from '@core/services/dialog.service';
import { MatDialog } from '@angular/material/dialog';
import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component';
import { mergeMap } from 'rxjs/operators';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models';
@Component({
selector: 'tb-profile-lwm2m-bootstrap-config-servers',
templateUrl: './lwm2m-bootstrap-config-servers.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Lwm2mBootstrapConfigServersComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => Lwm2mBootstrapConfigServersComponent),
multi: true,
}
]
})
export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValueAccessor {
bootstrapConfigServersFormGroup: FormGroup;
@Input()
disabled: boolean;
private valueChangeSubscription: Subscription = null;
private propagateChange = (v: any) => { };
constructor(public translate: TranslateService,
public matDialog: MatDialog,
private dialogService: DialogService,
private deviceProfileService: DeviceProfileService,
private fb: FormBuilder) {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.bootstrapConfigServersFormGroup = this.fb.group({
serverConfigs: this.fb.array([])
});
}
serverConfigsFromArray(): FormArray {
return this.bootstrapConfigServersFormGroup.get('serverConfigs') as FormArray;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.bootstrapConfigServersFormGroup.disable({emitEvent: false});
} else {
this.bootstrapConfigServersFormGroup.enable({emitEvent: false});
}
}
writeValue(serverConfigs: Array<ServerSecurityConfig> | null): void {
if (this.valueChangeSubscription) {
this.valueChangeSubscription.unsubscribe();
}
const serverConfigsControls: Array<AbstractControl> = [];
if (serverConfigs) {
serverConfigs.forEach((serverConfig) => {
serverConfigsControls.push(this.fb.control(serverConfig));
});
}
this.bootstrapConfigServersFormGroup.setControl('serverConfigs', this.fb.array(serverConfigsControls));
if (this.disabled) {
this.bootstrapConfigServersFormGroup.disable({emitEvent: false});
} else {
this.bootstrapConfigServersFormGroup.enable({emitEvent: false});
}
this.valueChangeSubscription = this.bootstrapConfigServersFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
trackByParams(index: number): number {
return index;
}
removeServerConfig($event: Event, index: number) {
if ($event) {
$event.stopPropagation();
$event.preventDefault();
}
this.dialogService.confirm(
this.translate.instant('device-profile.lwm2m.delete-server-title'),
this.translate.instant('device-profile.lwm2m.delete-server-text'),
this.translate.instant('action.no'),
this.translate.instant('action.yes'),
true
).subscribe((result) => {
if (result) {
this.serverConfigsFromArray().removeAt(index);
}
});
}
addServerConfig(): void {
const addDialogObs = this.isBootstrapAdded() ? of(false) :
this.matDialog.open<Lwm2mBootstrapAddConfigServerDialogComponent>(Lwm2mBootstrapAddConfigServerDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog']
}).afterClosed();
const addServerConfigObs = addDialogObs.pipe(
mergeMap((isBootstrap) => {
if (isBootstrap === null) {
return of(null);
}
return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(isBootstrap, Lwm2mSecurityType.NO_SEC);
})
);
addServerConfigObs.subscribe((serverConfig) => {
if (serverConfig) {
serverConfig.securityMode = Lwm2mSecurityType.NO_SEC;
this.serverConfigsFromArray().push(this.fb.control(serverConfig));
this.updateModel();
}
});
}
public validate(c: FormControl) {
return (this.bootstrapConfigServersFormGroup.valid) ? null : {
serverConfigs: {
valid: false,
},
};
}
public isBootstrapAdded() {
const serverConfigsArray = this.serverConfigsFromArray().getRawValue();
for (let i = 0; i < serverConfigsArray.length; i++) {
if (serverConfigsArray[i].bootstrapServerIs) {
return true;
}
}
return false;
}
private updateModel() {
const serverConfigs: Array<ServerSecurityConfig> = this.serverConfigsFromArray().value;
this.propagateChange(serverConfigs);
}
}

257
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html

@ -15,99 +15,170 @@
limitations under the License.
-->
<section [formGroup]="serverFormGroup">
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label>
<mat-select formControlName="securityMode">
<mat-option *ngFor="let securityMode of securityConfigLwM2MTypes"
[value]="securityMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label>
<input matInput type="text" formControlName="host" required>
<mat-error *ngIf="serverFormGroup.get('host').hasError('required')">
{{ 'device-profile.lwm2m.server-host-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label>
<input matInput type="number" formControlName="port" required min="0" max="65535">
<mat-error *ngIf="serverFormGroup.get('port').hasError('required')">
{{ 'device-profile.lwm2m.server-port-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('port').hasError('pattern')">
{{ 'device-profile.lwm2m.server-port-pattern' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('port').hasError('min') ||
<mat-expansion-panel [formGroup]="serverFormGroup" #serverPanel>
<mat-expansion-panel-header>
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<mat-panel-title>
<div fxLayout="row" fxFlex fxLayoutAlign="start center">
<div style="min-width:150px">{{ (serverFormGroup.get('bootstrapServerIs').value ?
'device-profile.lwm2m.bootstrap-server' : 'device-profile.lwm2m.lwm2m-server') | translate }}</div>
<div *ngIf="!serverPanel.expanded" fxLayout="row" style="font-size:14px">
<div style="margin-left:32px">{{ ('device-profile.lwm2m.short-id' | translate) + ': ' }}
<span style="font-style: italic">{{ serverFormGroup.get('shortServerId').value }}</span>
</div>
<div style="margin-left:32px">{{ ('device-profile.lwm2m.mode' | translate) + ': ' }}
<span style="font-style: italic">{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[serverFormGroup.get('securityMode').value]) }}</span>
</div>
</div>
</div>
</mat-panel-title>
<span fxFlex></span>
<button *ngIf="!disabled" mat-icon-button style="min-width: 40px;"
type="button"
(click)="removeServer.emit($event)"
matTooltip="{{ 'action.remove' | translate }}"
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<section>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label>
<mat-select formControlName="securityMode">
<mat-option *ngFor="let securityMode of securityConfigLwM2MTypes"
[value]="securityMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.short-id-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" min="1" max="65534" formControlName="shortServerId" required>
<mat-error *ngIf="serverFormGroup.get('shortServerId').hasError('required')">
{{ 'device-profile.lwm2m.short-id-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('shortServerId').hasError('pattern')">
{{ 'device-profile.lwm2m.short-id-pattern' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('shortServerId').hasError('min') ||
serverFormGroup.get('shortServerId').hasError('max')">
{{ 'device-profile.lwm2m.short-id-range' | translate }}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label>
<input matInput type="text" formControlName="host" required>
<mat-error *ngIf="serverFormGroup.get('host').hasError('required')">
{{ 'device-profile.lwm2m.server-host-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label>
<input matInput type="number" formControlName="port" required min="0" max="65535" readonly>
<mat-error *ngIf="serverFormGroup.get('port').hasError('required')">
{{ 'device-profile.lwm2m.server-port-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('port').hasError('pattern')">
{{ 'device-profile.lwm2m.server-port-pattern' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('port').hasError('min') ||
serverFormGroup.get('port').hasError('max')">
{{ 'device-profile.lwm2m.server-port-range' | translate }}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.short-id-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" min="1" max="65534" formControlName="serverId" required>
<mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')">
{{ 'device-profile.lwm2m.short-id-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('serverId').hasError('pattern')">
{{ 'device-profile.lwm2m.short-id-pattern' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('serverId').hasError('min') ||
serverFormGroup.get('serverId').hasError('max')">
{{ 'device-profile.lwm2m.short-id-range' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" formControlName="clientHoldOffTime" required min="0">
<mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')">
{{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('min') ||
{{ 'device-profile.lwm2m.server-port-range' | translate }}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" formControlName="clientHoldOffTime" required min="0">
<mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')">
{{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('min') ||
serverFormGroup.get('clientHoldOffTime').hasError('pattern')">
{{ 'device-profile.lwm2m.client-hold-off-time-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.account-after-timeout' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.account-after-timeout-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" formControlName="bootstrapServerAccountTimeout" required min="0">
<mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')">
{{ 'device-profile.lwm2m.account-after-timeout-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('min') ||
{{ 'device-profile.lwm2m.client-hold-off-time-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.account-after-timeout' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.account-after-timeout-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" formControlName="bootstrapServerAccountTimeout" required min="0">
<mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')">
{{ 'device-profile.lwm2m.account-after-timeout-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('min') ||
serverFormGroup.get('bootstrapServerAccountTimeout').hasError('pattern')">
{{ 'device-profile.lwm2m.account-after-timeout-pattern' | translate }}
</mat-error>
</mat-form-field>
</div>
<div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509">
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{ publicKeyOrIdTooltipNamesMap.get(serverFormGroup.get('securityMode').value) | translate }}">help</mat-icon>
<textarea matInput
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1" required
formControlName="serverPublicKey"
required>
</textarea>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')">
{{ 'device-profile.lwm2m.server-public-key-required' | translate }}
</mat-error>
</mat-form-field>
</div>
</section>
{{ 'device-profile.lwm2m.account-after-timeout-pattern' | translate }}
</mat-error>
</mat-form-field>
</div>
<div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509">
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{ publicKeyOrIdTooltipNamesMap.get(serverFormGroup.get('securityMode').value) | translate }}">help</mat-icon>
<textarea matInput
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1" required
formControlName="serverPublicKey"
required>
</textarea>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')">
{{ 'device-profile.lwm2m.server-public-key-required' | translate }}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.lifetime' | translate }}</mat-label>
<input matInput type="number" min="0" formControlName="lifetime" required>
<mat-error *ngIf="serverFormGroup.get('lifetime').hasError('required')">
{{ 'device-profile.lwm2m.lifetime-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('lifetime').hasError('pattern') ||
serverFormGroup.get('lifetime').hasError('min')">
{{ 'device-profile.lwm2m.lifetime-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.default-min-period' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.default-min-period-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" min="0" formControlName="defaultMinPeriod" required>
<mat-error *ngIf="serverFormGroup.get('defaultMinPeriod').hasError('required')">
{{ 'device-profile.lwm2m.default-min-period-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('defaultMinPeriod').hasError('pattern') ||
serverFormGroup.get('defaultMinPeriod').hasError('min')">
{{ 'device-profile.lwm2m.default-min-period-pattern' | translate }}
</mat-error>
</mat-form-field>
</div>
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.binding' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.binding-tooltip' | translate }}">help</mat-icon>
<mat-select formControlName="binding">
<mat-option *ngFor="let bindingMode of bindingModeTypes" [value]="bindingMode">
{{ bindingModeTypeNamesMap.get(bindingMode) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox formControlName="notifIfDisabled" color="primary">
{{ 'device-profile.lwm2m.notification-storing' | translate }}
</mat-checkbox>
</section>
</ng-template>
</mat-expansion-panel>

44
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { Component, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
@ -26,8 +26,7 @@ import {
Validators
} from '@angular/forms';
import {
DEFAULT_PORT_BOOTSTRAP_NO_SEC,
DEFAULT_PORT_SERVER_NO_SEC,
BingingMode, BingingModeTranslationsMap,
ServerSecurityConfig
} from './lwm2m-profile-config.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
@ -65,14 +64,16 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
private isDataLoadedIntoCache = false;
serverFormGroup: FormGroup;
bindingModeTypes = Object.values(BingingMode);
bindingModeTypeNamesMap = BingingModeTranslationsMap;
securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.keys(Lwm2mSecurityType);
credentialTypeLwM2MNamesMap = Lwm2mSecurityTypeTranslationMap;
publicKeyOrIdTooltipNamesMap = Lwm2mPublicKeyOrIdTooltipTranslationsMap;
currentSecurityMode = null;
@Input()
isBootstrapServer = false;
@Output()
removeServer = new EventEmitter();
private propagateChange = (v: any) => { };
@ -83,19 +84,29 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
ngOnInit(): void {
this.serverFormGroup = this.fb.group({
host: ['', Validators.required],
port: [this.isBootstrapServer ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC,
[Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]],
port: ['', [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]],
securityMode: [Lwm2mSecurityType.NO_SEC],
serverPublicKey: [''],
clientHoldOffTime: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
serverId: ['', [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]],
shortServerId: ['', [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]],
bootstrapServerAccountTimeout: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
binding: [''],
lifetime: [null, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
notifIfDisabled: [],
defaultMinPeriod: [null, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
bootstrapServerIs: []
});
this.serverFormGroup.get('securityMode').valueChanges.pipe(
tap(securityMode => this.updateValidate(securityMode)),
tap((securityMode) => {
this.currentSecurityMode = securityMode;
this.updateValidate(securityMode);
}),
mergeMap(securityMode => this.getLwm2mBootstrapSecurityInfo(securityMode)),
takeUntil(this.destroy$)
).subscribe(serverSecurityConfig => {
if (this.currentSecurityMode !== Lwm2mSecurityType.NO_SEC) {
this.changeSecurityHostPortFields(serverSecurityConfig);
}
this.serverFormGroup.patchValue(serverSecurityConfig, {emitEvent: false});
});
this.serverFormGroup.valueChanges.pipe(
@ -114,8 +125,13 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
if (serverData) {
this.serverFormGroup.patchValue(serverData, {emitEvent: false});
this.updateValidate(serverData.securityMode);
if (serverData.securityHost && serverData.securityPort) {
delete serverData.securityHost;
delete serverData.securityPort;
this.propagateChangeState(this.serverFormGroup.getRawValue());
}
}
if (!this.isDataLoadedIntoCache){
if (!this.isDataLoadedIntoCache) {
this.getLwm2mBootstrapSecurityInfo().subscribe(value => {
if (!serverData) {
this.serverFormGroup.patchValue(value);
@ -171,11 +187,17 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
}
private getLwm2mBootstrapSecurityInfo(securityMode = Lwm2mSecurityType.NO_SEC): Observable<ServerSecurityConfig> {
return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(this.isBootstrapServer, securityMode).pipe(
return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(
this.serverFormGroup.get('bootstrapServerIs').value, securityMode).pipe(
tap(() => this.isDataLoadedIntoCache = true)
);
}
private changeSecurityHostPortFields(serverData: ServerSecurityConfig): void {
serverData.port = serverData.securityPort;
serverData.host = serverData.securityHost;
}
validate(): ValidationErrors | null {
return this.serverFormGroup.valid ? null : {
serverFormGroup: true

90
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html

@ -33,93 +33,9 @@
</mat-tab>
<mat-tab label="{{ 'device-profile.lwm2m.bootstrap-tab' | translate }}">
<section [formGroup]="lwm2mDeviceProfileFormGroup">
<section formGroupName="bootstrap" style="padding: 20px 2px">
<mat-accordion multi="true">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>{{ 'device-profile.lwm2m.server' | translate }}</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent formGroupName="servers">
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.short-id-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" min="1" max="65534" formControlName="shortId" required>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('required')">
{{ 'device-profile.lwm2m.short-id-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('min') ||
lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('max')">
{{ 'device-profile.lwm2m.short-id-range' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('pattern')">
{{ 'device-profile.lwm2m.short-id-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.lifetime' | translate }}</mat-label>
<input matInput type="number" min="0" formControlName="lifetime" required>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.lifetime').hasError('required')">
{{ 'device-profile.lwm2m.lifetime-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.lifetime').hasError('pattern') ||
lwm2mDeviceProfileFormGroup.get('bootstrap.servers.lifetime').hasError('min')">
{{ 'device-profile.lwm2m.lifetime-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.default-min-period' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.default-min-period-tooltip' | translate }}">help</mat-icon>
<input matInput type="number" min="0" formControlName="defaultMinPeriod" required>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.defaultMinPeriod').hasError('required')">
{{ 'device-profile.lwm2m.default-min-period-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.defaultMinPeriod').hasError('pattern') ||
lwm2mDeviceProfileFormGroup.get('bootstrap.servers.defaultMinPeriod').hasError('min')">
{{ 'device-profile.lwm2m.default-min-period-pattern' | translate }}
</mat-error>
</mat-form-field>
</div>
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.binding' | translate }}</mat-label>
<mat-icon *ngIf="!disabled" class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"
matTooltip="{{'device-profile.lwm2m.binding-tooltip' | translate }}">help</mat-icon>
<mat-select formControlName="binding">
<mat-option *ngFor="let bindingMode of bindingModeTypes" [value]="bindingMode">
{{ bindingModeTypeNamesMap.get(bindingMode) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox formControlName="notifIfDisabled" color="primary">
{{ 'device-profile.lwm2m.notification-storing' | translate }}
</mat-checkbox>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>{{ 'device-profile.lwm2m.bootstrap-server' | translate }}</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-profile-lwm2m-device-config-server
formControlName="bootstrapServer"
[isBootstrapServer]="true">
</tb-profile-lwm2m-device-config-server>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>{{ 'device-profile.lwm2m.lwm2m-server' | translate }}</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-profile-lwm2m-device-config-server
formControlName="lwm2mServer"
[isBootstrapServer]="false">
</tb-profile-lwm2m-device-config-server>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
<section style="padding: 20px 2px">
<tb-profile-lwm2m-bootstrap-config-servers formControlName="bootstrap">
</tb-profile-lwm2m-bootstrap-config-servers>
</section>
</section>
</mat-tab>

56
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts

@ -28,20 +28,11 @@ import {
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
ATTRIBUTE,
BingingMode,
BingingModeTranslationsMap,
DEFAULT_BINDING,
DEFAULT_EDRX_CYCLE,
DEFAULT_FW_UPDATE_RESOURCE,
DEFAULT_ID_SERVER,
DEFAULT_LIFE_TIME,
DEFAULT_MIN_PERIOD,
DEFAULT_NOTIF_IF_DESIBLED,
DEFAULT_PAGING_TRANSMISSION_WINDOW,
DEFAULT_PSM_ACTIVITY_TIMER,
DEFAULT_SW_UPDATE_RESOURCE,
getDefaultBootstrapServerSecurityConfig,
getDefaultLwM2MServerSecurityConfig,
Instance,
INSTANCES,
KEY_NAME,
@ -60,6 +51,7 @@ import { Direction } from '@shared/models/page/sort-order';
import _ from 'lodash';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models';
@Component({
selector: 'tb-profile-lwm2m-device-transport-configuration',
@ -83,8 +75,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private requiredValue: boolean;
private destroy$ = new Subject();
bindingModeTypes = Object.values(BingingMode);
bindingModeTypeNamesMap = BingingModeTranslationsMap;
lwm2mDeviceProfileFormGroup: FormGroup;
configurationValue: Lwm2mProfileConfigModels;
sortFunction: (key: string, value: object) => object;
@ -98,6 +88,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
isAdd: boolean;
private propagateChange = (v: any) => {
}
@ -106,17 +99,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.lwm2mDeviceProfileFormGroup = this.fb.group({
objectIds: [null],
observeAttrTelemetry: [null],
bootstrap: this.fb.group({
servers: this.fb.group({
binding: [DEFAULT_BINDING],
shortId: [DEFAULT_ID_SERVER, [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]],
lifetime: [DEFAULT_LIFE_TIME, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
notifIfDisabled: [DEFAULT_NOTIF_IF_DESIBLED, []],
defaultMinPeriod: [DEFAULT_MIN_PERIOD, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
}),
bootstrapServer: [null, Validators.required],
lwm2mServer: [null, Validators.required]
}),
bootstrap: [[]],
clientLwM2mSettings: this.fb.group({
clientOnlyObserveAfterConnect: [1, []],
fwUpdateStrategy: [1, []],
@ -187,9 +170,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
async writeValue(value: Lwm2mProfileConfigModels | null) {
if (isDefinedAndNotNull(value) && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) {
this.configurationValue = value;
const defaultFormSettings = value.clientLwM2mSettings.fwUpdateStrategy === 1 &&
isUndefined(value.clientLwM2mSettings.fwUpdateResource);
if (defaultFormSettings) {
if (this.isAdd) {
await this.defaultProfileConfig();
}
this.initWriteValue();
@ -203,23 +184,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
private async defaultProfileConfig(): Promise<void> {
let bootstrap: ServerSecurityConfig;
let lwm2m: ServerSecurityConfig;
try {
[bootstrap, lwm2m] = await Promise.all([
this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(true).toPromise(),
this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(false).toPromise()
]);
} catch (e) {
bootstrap = getDefaultBootstrapServerSecurityConfig();
lwm2m = getDefaultLwM2MServerSecurityConfig();
const lwm2m: ServerSecurityConfig = await this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(false).toPromise();
if (lwm2m) {
lwm2m.securityMode = Lwm2mSecurityType.NO_SEC;
}
this.configurationValue.bootstrap.bootstrapServer = bootstrap;
this.configurationValue.bootstrap.lwm2mServer = lwm2m;
this.configurationValue.bootstrap = [lwm2m];
this.lwm2mDeviceProfileFormGroup.patchValue({
bootstrap: this.configurationValue.bootstrap
}, {emitEvent: false});
}, {emitEvent: true});
}
private initWriteValue = (): void => {
@ -282,12 +254,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
private updateDeviceProfileValue(config): void {
if (this.lwm2mDeviceProfileFormGroup.valid) {
if (this.lwm2mDeviceProfileFormGroup.valid && config.observeAttrTelemetry) {
this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry);
}
this.configurationValue.bootstrap.bootstrapServer = config.bootstrap.bootstrapServer;
this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer;
this.configurationValue.bootstrap.servers = config.bootstrap.servers;
this.configurationValue.bootstrap = config.bootstrap;
this.configurationValue.clientLwM2mSettings = config.clientLwM2mSettings;
this.updateModel();
}

6
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts

@ -22,7 +22,9 @@ import { Lwm2mObserveAttrTelemetryResourcesComponent } from './lwm2m-observe-att
import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component';
import { Lwm2mAttributesComponent } from './lwm2m-attributes.component';
import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component';
import { Lwm2mBootstrapConfigServersComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component';
import { Lwm2mDeviceConfigServerComponent } from './lwm2m-device-config-server.component';
import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component';
import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-instances-dialog.component';
import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component';
import { CommonModule } from '@angular/common';
@ -40,7 +42,9 @@ import { DeviceProfileCommonModule } from '@home/components/profile/device/commo
Lwm2mAttributesDialogComponent,
Lwm2mAttributesComponent,
Lwm2mAttributesKeyListComponent,
Lwm2mBootstrapConfigServersComponent,
Lwm2mDeviceConfigServerComponent,
Lwm2mBootstrapAddConfigServerDialogComponent,
Lwm2mObjectAddInstancesDialogComponent,
Lwm2mObjectAddInstancesListComponent,
Lwm2mObserveAttrTelemetryInstancesComponent
@ -58,7 +62,9 @@ import { DeviceProfileCommonModule } from '@home/components/profile/device/commo
Lwm2mAttributesDialogComponent,
Lwm2mAttributesComponent,
Lwm2mAttributesKeyListComponent,
Lwm2mBootstrapConfigServersComponent,
Lwm2mDeviceConfigServerComponent,
Lwm2mBootstrapAddConfigServerDialogComponent,
Lwm2mObjectAddInstancesDialogComponent,
Lwm2mObjectAddInstancesListComponent,
Lwm2mObserveAttrTelemetryInstancesComponent

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

@ -96,6 +96,18 @@ export const AttributeNameTranslationMap = new Map<AttributeName, string>(
]
);
export enum ServerConfigType {
LWM2M = 'LWM2M',
BOOTSTRAP = 'BOOTSTRAP'
}
export const ServerConfigTypeTranslationMap = new Map<ServerConfigType, string>(
[
[ServerConfigType.LWM2M, 'device-profile.lwm2m.lwm2m-server'],
[ServerConfigType.BOOTSTRAP, 'device-profile.lwm2m.bootstrap-server']
]
);
export enum PowerMode {
PSM = 'PSM',
DRX = 'DRX',
@ -110,22 +122,21 @@ export const PowerModeTranslationMap = new Map<PowerMode, string>(
]
);
export interface BootstrapServersSecurityConfig {
shortId: number;
lifetime: number;
defaultMinPeriod: number;
notifIfDisabled: boolean;
binding: string;
}
export interface ServerSecurityConfig {
host?: string;
port?: number;
securityMode: Lwm2mSecurityType;
securityHost?: string;
securityPort?: number;
serverPublicKey?: string;
clientHoldOffTime?: number;
serverId?: number;
shortServerId?: number;
bootstrapServerAccountTimeout: number;
lifetime: number;
defaultMinPeriod: number;
notifIfDisabled: boolean;
binding: string;
bootstrapServerIs: boolean;
}
export interface ServerSecurityConfigInfo extends ServerSecurityConfig {
@ -134,16 +145,10 @@ export interface ServerSecurityConfigInfo extends ServerSecurityConfig {
bootstrapServerIs: boolean;
}
interface BootstrapSecurityConfig {
servers: BootstrapServersSecurityConfig;
bootstrapServer: ServerSecurityConfig;
lwm2mServer: ServerSecurityConfig;
}
export interface Lwm2mProfileConfigModels {
clientLwM2mSettings: ClientLwM2mSettings;
observeAttr: ObservableAttributes;
bootstrap: BootstrapSecurityConfig;
bootstrap: Array<ServerSecurityConfig>;
}
export interface ClientLwM2mSettings {
@ -167,35 +172,6 @@ export interface ObservableAttributes {
attributeLwm2m: AttributesNameValueMap;
}
export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig {
return {
shortId: DEFAULT_ID_SERVER,
lifetime: DEFAULT_LIFE_TIME,
defaultMinPeriod: DEFAULT_MIN_PERIOD,
notifIfDisabled: DEFAULT_NOTIF_IF_DESIBLED,
binding: DEFAULT_BINDING
};
}
export function getDefaultBootstrapServerSecurityConfig(): ServerSecurityConfig {
return {
bootstrapServerAccountTimeout: DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT,
clientHoldOffTime: DEFAULT_CLIENT_HOLD_OFF_TIME,
host: DEFAULT_LOCAL_HOST_NAME,
port: DEFAULT_PORT_BOOTSTRAP_NO_SEC,
securityMode: Lwm2mSecurityType.NO_SEC,
serverId: DEFAULT_ID_BOOTSTRAP,
serverPublicKey: ''
};
}
export function getDefaultLwM2MServerSecurityConfig(): ServerSecurityConfig {
const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig();
DefaultLwM2MServerSecurityConfig.port = DEFAULT_PORT_SERVER_NO_SEC;
DefaultLwM2MServerSecurityConfig.serverId = DEFAULT_ID_SERVER;
return DefaultLwM2MServerSecurityConfig;
}
export function getDefaultProfileObserveAttrConfig(): ObservableAttributes {
return {
observe: [],

1
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html

@ -133,6 +133,7 @@
</mat-form-field>
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
isAdd="true"
required>
</tb-device-profile-transport-configuration>
</form>

1
ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html

@ -35,6 +35,7 @@
<div formGroupName="profileData">
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
[isAdd] = "isTransportTypeChanged"
required>
</tb-device-profile-transport-configuration>
</div>

5
ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts

@ -38,12 +38,17 @@ export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfil
deviceTransportTypeHints = deviceTransportTypeHintMap;
isTransportTypeChanged = false;
constructor(protected store: Store<AppState>) {
super(store);
}
ngOnInit() {
super.ngOnInit();
this.detailsForm.get('transportType').valueChanges.subscribe(() => {
this.isTransportTypeChanged = true;
});
}
}

9
ui-ngx/src/app/shared/models/device.models.ts

@ -31,9 +31,6 @@ import { OtaPackageId } from '@shared/models/id/ota-package-id';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { DataType } from '@shared/models/constants';
import {
getDefaultBootstrapServerSecurityConfig,
getDefaultBootstrapServersSecurityConfig,
getDefaultLwM2MServerSecurityConfig,
getDefaultProfileClientLwM2mSettingsConfig,
getDefaultProfileObserveAttrConfig,
PowerMode
@ -384,11 +381,7 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
case DeviceTransportType.LWM2M:
const lwm2mTransportConfiguration: Lwm2mDeviceProfileTransportConfiguration = {
observeAttr: getDefaultProfileObserveAttrConfig(),
bootstrap: {
servers: getDefaultBootstrapServersSecurityConfig(),
bootstrapServer: getDefaultBootstrapServerSecurityConfig(),
lwm2mServer: getDefaultLwM2MServerSecurityConfig()
},
bootstrap: [],
clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig()
};
transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};

14
ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts

@ -67,14 +67,9 @@ export interface ServerSecurityConfig {
clientSecretKey?: string;
}
interface BootstrapSecurityConfig {
bootstrapServer: ServerSecurityConfig;
lwm2mServer: ServerSecurityConfig;
}
export interface Lwm2mSecurityConfigModels {
client: ClientSecurityConfig;
bootstrap: BootstrapSecurityConfig;
bootstrap: Array<ServerSecurityConfig>;
}
@ -84,10 +79,9 @@ export function getLwm2mSecurityConfigModelsDefault(): Lwm2mSecurityConfigModels
securityConfigClientMode: Lwm2mSecurityType.NO_SEC,
endpoint: ''
},
bootstrap: {
bootstrapServer: getDefaultServerSecurityConfig(),
lwm2mServer: getDefaultServerSecurityConfig()
}
bootstrap: [
getDefaultServerSecurityConfig()
]
};
}

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

@ -1310,16 +1310,18 @@
"edit-attribute": "Edit attribute",
"view-attribute": "View attribute",
"remove-attribute": "Remove attribute",
"delete-server-text": "Be careful, after the confirmation the server configuration will become unrecoverable.",
"delete-server-title": "Are you sure you want to delete the server?",
"mode": "Security config mode",
"bootstrap-tab": "Bootstrap",
"bootstrap-server-legend": "Bootstrap Server (ShortId...)",
"lwm2m-server-legend": "LwM2M Server (ShortId...)",
"server": "Server",
"short-id": "Short ID",
"short-id-tooltip": "Server ShortID must be equal Security ShortID ",
"short-id-required": "Short ID is required.",
"short-id-range": "Short ID should be in a range from 1 to 65534.",
"short-id-pattern": "Short ID must be a positive integer.",
"short-id": "Short server ID",
"short-id-tooltip": "Server short Id. Used as link to associate server Object Instance.\nThis identifier uniquely identifies each LwM2M Server configured for the LwM2M Client.\nResource MUST be set when the Bootstrap-Server Resource has a value of 'false'.\nThe values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.",
"short-id-required": "Short server ID is required.",
"short-id-range": "Short server ID should be in a range from 1 to 65534.",
"short-id-pattern": "Short server ID must be a positive integer.",
"lifetime": "Client registration lifetime",
"lifetime-required": "Client registration lifetime is required.",
"lifetime-pattern": "Client registration lifetime must be a positive integer.",
@ -1361,6 +1363,11 @@
"account-after-timeout-required": "Account after the timeout is required.",
"account-after-timeout-pattern": "Account after the timeout must be a positive integer.",
"account-after-timeout-tooltip": "Bootstrap-Server Account after the timeout value given by this resource.",
"server-type": "Server type",
"add-new-server-title": "Add new server config",
"add-server-config": "Add server config",
"add-lwm2m-server-config": "Add LwM2M server",
"no-config-servers": "No servers configured",
"others-tab": "Other settings",
"client-strategy": "Client strategy when connecting",
"client-strategy-label": "Strategy",

Loading…
Cancel
Save