Browse Source

UI: Refactoring for the build rule node ui (#4460)

* UI: Refactoring LWM2M; fixed build ui rule node

* UI: Refactoring LWM2M security config
pull/4525/head
Vladyslav 5 years ago
committed by GitHub
parent
commit
9dcb7b9b5b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.html
  2. 75
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts
  3. 79
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.html
  4. 20
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss
  5. 176
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts
  6. 143
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html
  7. 34
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss
  8. 257
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts
  9. 6
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  10. 27
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts
  11. 30
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts
  12. 41
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts
  13. 18
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts
  14. 31
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
  15. 14
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.ts
  16. 11
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts
  17. 47
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts
  18. 19
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts
  19. 24
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts
  20. 26
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts
  21. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
  22. 6
      ui-ngx/src/app/modules/home/pages/device/device.module.ts
  23. 101
      ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.html
  24. 142
      ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.ts
  25. 180
      ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.html
  26. 382
      ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.ts
  27. 66
      ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts
  28. 15
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
ui-ngx/src/app/modules/home/components/device/device-credentials.component.html

@ -20,7 +20,7 @@
<mat-label translate>device.credentials-type</mat-label> <mat-label translate>device.credentials-type</mat-label>
<mat-select formControlName="credentialsType"> <mat-select formControlName="credentialsType">
<mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType"> <mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">
{{ credentialTypeNamesMap.get(deviceCredentialsType[credentialsType]) }} {{ credentialTypeNamesMap.get(credentialsType) }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@ -85,7 +85,7 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-value</mat-label> <mat-label translate>device.lwm2m-value</mat-label>
<textarea matInput formControlName="credentialsValue" rows="10" required <textarea matInput formControlName="credentialsValue" rows="10" required
[matTooltip]="lwm2mCredentialsValueTip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))" [matTooltip]="lwm2mCredentialsValueTooltip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
matTooltipPosition="above" matTooltipPosition="above"
></textarea> ></textarea>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')"> <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">

75
ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, forwardRef, Input, OnDestroy, OnInit} from '@angular/core'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
FormBuilder, FormBuilder,
@ -33,9 +33,9 @@ import {
DeviceCredentials, DeviceCredentials,
DeviceCredentialsType DeviceCredentialsType
} from '@shared/models/device.models'; } from '@shared/models/device.models';
import {Subscription} from 'rxjs'; import { Subject } from 'rxjs';
import {distinctUntilChanged} from 'rxjs/operators'; import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import {SecurityConfigComponent} from '@home/pages/device/lwm2m/security-config.component'; import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import { import {
ClientSecurityConfig, ClientSecurityConfig,
DEFAULT_END_POINT, DEFAULT_END_POINT,
@ -44,10 +44,10 @@ import {
getDefaultSecurityConfig, getDefaultSecurityConfig,
JSON_ALL_CONFIG, JSON_ALL_CONFIG,
validateSecurityConfig validateSecurityConfig
} from '@home/pages/device/lwm2m/security-config.models'; } from '@shared/models/lwm2m-security-config.models';
import {TranslateService} from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import {MatDialog} from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import {isDefinedAndNotNull} from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
@Component({ @Component({
selector: 'tb-device-credentials', selector: 'tb-device-credentials',
@ -67,20 +67,16 @@ import {isDefinedAndNotNull} from '@core/utils';
}) })
export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy {
deviceCredentialsFormGroup: FormGroup;
subscriptions: Subscription[] = [];
@Input() @Input()
disabled: boolean; disabled: boolean;
deviceCredentials: DeviceCredentials = null; private destroy$ = new Subject();
submitted = false; deviceCredentialsFormGroup: FormGroup;
deviceCredentialsType = DeviceCredentialsType; deviceCredentialsType = DeviceCredentialsType;
credentialsTypes = Object.keys(DeviceCredentialsType); credentialsTypes = Object.values(DeviceCredentialsType);
credentialTypeNamesMap = credentialTypeNames; credentialTypeNamesMap = credentialTypeNames;
@ -102,16 +98,17 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])}) }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])})
}); });
this.deviceCredentialsFormGroup.get('credentialsBasic').disable(); this.deviceCredentialsFormGroup.get('credentialsBasic').disable();
this.subscriptions.push( this.deviceCredentialsFormGroup.valueChanges.pipe(
this.deviceCredentialsFormGroup.valueChanges.pipe(distinctUntilChanged()).subscribe(() => { distinctUntilChanged(),
this.updateView(); takeUntil(this.destroy$)
}) ).subscribe(() => {
); this.updateView();
this.subscriptions.push( });
this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.subscribe(() => { this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.pipe(
this.credentialsTypeChanged(); takeUntil(this.destroy$)
}) ).subscribe((type) => {
); this.credentialsTypeChanged(type);
});
} }
ngOnInit(): void { ngOnInit(): void {
@ -121,12 +118,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
} }
ngOnDestroy() { ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe()); this.destroy$.next();
this.destroy$.complete();
} }
writeValue(value: DeviceCredentials | null): void { writeValue(value: DeviceCredentials | null): void {
if (isDefinedAndNotNull(value)) { if (isDefinedAndNotNull(value)) {
this.deviceCredentials = value;
let credentialsBasic = {clientId: null, userName: null, password: null}; let credentialsBasic = {clientId: null, userName: null, password: null};
let credentialsValue = null; let credentialsValue = null;
if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) { if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
@ -179,10 +176,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}; };
} }
credentialsTypeChanged(): void { credentialsTypeChanged(credentialsType: DeviceCredentialsType): void {
const credentialsValue = credentialsType === DeviceCredentialsType.LWM2M_CREDENTIALS ? this.lwm2mDefaultConfig : null;
this.deviceCredentialsFormGroup.patchValue({ this.deviceCredentialsFormGroup.patchValue({
credentialsId: null, credentialsId: null,
credentialsValue: JSON.stringify(getDefaultSecurityConfig(), null, 2), credentialsValue,
credentialsBasic: {clientId: '', userName: '', password: ''} credentialsBasic: {clientId: '', userName: '', password: ''}
}); });
this.updateValidators(); this.updateValidators();
@ -264,7 +262,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
} }
} }
const credentialsId = this.deviceCredentialsFormGroup.get('credentialsId').value || DEFAULT_END_POINT; const credentialsId = this.deviceCredentialsFormGroup.get('credentialsId').value || DEFAULT_END_POINT;
this.dialog.open<SecurityConfigComponent, DeviceCredentialsDialogLwm2mData, object>(SecurityConfigComponent, { this.dialog.open<SecurityConfigLwm2mComponent, DeviceCredentialsDialogLwm2mData, object>(SecurityConfigLwm2mComponent, {
disableClose: true, disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
@ -275,8 +273,8 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
(res) => { (res) => {
if (res) { if (res) {
this.deviceCredentialsFormGroup.patchValue({ this.deviceCredentialsFormGroup.patchValue({
credentialsValue: this.isDefautLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]), credentialsValue: this.isDefaultLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]),
credentialsId: this.isDefautLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('') credentialsId: this.isDefaultLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('')
}); });
this.deviceCredentialsFormGroup.get('credentialsValue').markAsDirty(); this.deviceCredentialsFormGroup.get('credentialsValue').markAsDirty();
} }
@ -284,16 +282,19 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
); );
} }
private isDefautLw2mResponse(response: object): boolean { private isDefaultLw2mResponse(response: object): boolean {
return Object.keys(response).length === 0 || JSON.stringify(response) === '[{}]'; return Object.keys(response).length === 0 || JSON.stringify(response) === '[{}]';
} }
private lwm2mConfigJsonValidator(control: FormControl) { private lwm2mConfigJsonValidator(control: FormControl) {
return validateSecurityConfig(control.value) ? null: {jsonError: {parsedJson: "error"}}; return validateSecurityConfig(control.value) ? null : {jsonError: {parsedJson: 'error'}};
}
private get lwm2mDefaultConfig(): string {
return JSON.stringify(getDefaultSecurityConfig(), null, 2);
} }
lwm2mCredentialsValueTip (flag: boolean): string { lwm2mCredentialsValueTooltip(flag: boolean): string {
let jsonConfigDef = JSON.stringify(getDefaultSecurityConfig(), null, 2); return !flag ? '' : 'Example (mode=\"NoSec\"):\n\r ' + this.lwm2mDefaultConfig;
return !flag ? "" : 'Example (mode=\"NoSec\"):\n\r ' + jsonConfigDef;
} }
} }

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

@ -0,0 +1,79 @@
<!--
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.
-->
<section [formGroup]="serverFormGroup" fxLayout="column">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityMode">
<mat-option *ngFor="let securityType of securityConfigLwM2MTypes" [value]="securityType">
{{ lwm2mSecurityTypeTranslationMap.get(securityType) }}
</mat-option>
</mat-select>
</mat-form-field>
<div [fxShow]="serverFormGroup.get('securityMode').value !== securityConfigLwM2MType.NO_SEC">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}</mat-label>
<textarea matInput
#clientPublicKeyOrId
[maxlength]="lenMaxClientPublicKeyOrId"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="clientPublicKeyOrId"
required>
</textarea>
<mat-hint align="end">{{clientPublicKeyOrId.value?.length || 0}}/{{lenMaxClientPublicKeyOrId}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('required')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientPublicKeyOrId').hasError('maxlength') ||
serverFormGroup.get('clientPublicKeyOrId').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-length' | translate: {
count: lenMaxClientPublicKeyOrId
} }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-secret-key' | translate }}</mat-label>
<textarea matInput
#clientSecretKey
[maxlength]="lengthClientSecretKey"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="clientSecretKey"
required>
</textarea>
<mat-hint align="end">{{clientSecretKey.value?.length || 0}}/{{lengthClientSecretKey}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('required')">
{{ 'device.lwm2m-security-config.client-secret-key-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-secret-key-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientSecretKey').hasError('maxlength') ||
serverFormGroup.get('clientSecretKey').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-secret-key-length' | translate: {
count: lengthClientSecretKey
} }}
</mat-error>
</mat-form-field>
</div>
</section>

20
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss

@ -0,0 +1,20 @@
/**
* 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.
*/
:host ::ng-deep {
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

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

@ -0,0 +1,176 @@
///
/// 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, OnDestroy } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
Validators
} from '@angular/forms';
import {
KEY_REGEXP_HEX_DEC,
LEN_MAX_PRIVATE_KEY,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
LEN_MAX_PUBLIC_KEY_X509,
Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap,
ServerSecurityConfig
} from '@shared/models/lwm2m-security-config.models';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Component({
selector: 'tb-security-config-lwm2m-server',
templateUrl: './security-config-lwm2m-server.component.html',
styleUrls: ['./security-config-lwm2m-server.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
multi: true
}
]
})
export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator {
serverFormGroup: FormGroup;
securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.values(Lwm2mSecurityType);
lwm2mSecurityTypeTranslationMap = Lwm2mSecurityTypeTranslationMap;
lenMinClientPublicKeyOrId = 0;
lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
private destroy$ = new Subject();
private propagateChange = (v: any) => {};
constructor(private fb: FormBuilder) {
this.serverFormGroup = this.fb.group({
securityMode: [Lwm2mSecurityType.NO_SEC],
clientPublicKeyOrId: [''],
clientSecretKey: ['']
});
this.serverFormGroup.get('securityMode').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((securityMode) => {
this.updateValidate(securityMode);
});
this.serverFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
this.propagateChange(value);
});
}
writeValue(value: any): void {
if (value) {
this.updateValueFields(value);
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.serverFormGroup.disable({emitEvent: false});
} else {
this.serverFormGroup.enable({emitEvent: false});
}
}
validate(control): ValidationErrors | null {
return this.serverFormGroup.valid ? null : {
securityConfig: {valid: false}
};
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private updateValueFields(serverData: ServerSecurityConfig): void {
this.serverFormGroup.patchValue(serverData, {emitEvent: false});
this.updateValidate(serverData.securityMode, true);
}
private updateValidate(securityMode: Lwm2mSecurityType, initValue = false): void {
switch (securityMode) {
case Lwm2mSecurityType.NO_SEC:
this.serverFormGroup.get('clientPublicKeyOrId').clearValidators();
this.serverFormGroup.get('clientSecretKey').clearValidators();
break;
case Lwm2mSecurityType.PSK:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PSK;
this.setValidatorsSecurity(securityMode);
break;
case Lwm2mSecurityType.RPK:
this.lenMinClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
case Lwm2mSecurityType.X509:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_X509;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
}
this.serverFormGroup.get('clientPublicKeyOrId').updateValueAndValidity({emitEvent: false});
this.serverFormGroup.get('clientSecretKey').updateValueAndValidity({emitEvent: !initValue});
}
private setValidatorsSecurity = (securityMode: Lwm2mSecurityType): void => {
if (securityMode === Lwm2mSecurityType.PSK) {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([Validators.required]);
} else {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lenMinClientPublicKeyOrId),
Validators.maxLength(this.lenMaxClientPublicKeyOrId)
]);
}
this.serverFormGroup.get('clientSecretKey').setValidators([
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lengthClientSecretKey),
Validators.maxLength(this.lengthClientSecretKey)
]);
}
}

143
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html

@ -0,0 +1,143 @@
<!--
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]="lwm2mConfigFormGroup" (ngSubmit)="save()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary">
<h2>{{ 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>
<div mat-dialog-content>
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.endpoint</mat-label>
<input matInput type="text" formControlName="endPoint" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('endPoint').hasError('required')">
{{ 'device.lwm2m-security-config.endpoint-required' | translate }}
</mat-error>
</mat-form-field>
<mat-tab-group (selectedTabChange)="tabChanged($event)">
<mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityConfigClientMode">
<mat-option *ngFor="let securityConfigClientMode of securityConfigLwM2MTypes"
[value]="securityConfigClientMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityConfigClientMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<div *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.identity' | translate }}</mat-label>
<input matInput type="text" formControlName="identity" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('identity').hasError('required')">
{{ 'device.lwm2m-security-config.identity-required' | translate }}
</mat-error>
</mat-form-field>
</div>
<mat-form-field class="mat-block" *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.RPK ||
lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-label>{{ 'device.lwm2m-security-config.client-key' | translate }}</mat-label>
<textarea matInput
#key
maxlength="{{lenMaxKeyClient}}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="key"
required>
</textarea>
<mat-hint align="end">{{key.value?.length || 0}}/{{lenMaxKeyClient}}</mat-hint>
<mat-error *ngIf="lwm2mConfigFormGroup.get('key').hasError('required')">
{{ 'device.lwm2m-security-config.client-key-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mConfigFormGroup.get('key').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-key-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(lwm2mConfigFormGroup.get('key').hasError('maxlength') ||
lwm2mConfigFormGroup.get('key').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-key-length' | translate: {
count: lenMaxKeyClient
} }}
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="x509" color="primary"
*ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.X509">
{{ 'device.lwm2m-security-config.client-certificate' | translate }}
</mat-checkbox>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}">
<div style="padding: 2px;">
<mat-accordion multi="true" class="mat-body-1">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'device.lwm2m-security-config.bootstrap-server' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server
formControlName="bootstrapServer">
</tb-security-config-lwm2m-server>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'device.lwm2m-security-config.lwm2m-server' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server
formControlName="lwm2mServer">
</tb-security-config-lwm2m-server>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
</div>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.config-json-tab' | translate }}">
<ng-template matTabContent>
<tb-json-object-edit
[formControlName]="formControlNameJsonAllConfig"
label="{{ 'device.lwm2m-value' | translate }}"
readonly
[required]="true"
[fillHeight]="false">
</tb-json-object-edit>
</ng-template>
</mat-tab>
</mat-tab-group>
</div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || lwm2mConfigFormGroup.invalid || !lwm2mConfigFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div>
</form>

34
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss

@ -0,0 +1,34 @@
/**
* 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.
*/
:host {
mat-tab-group {
min-height: 330px;
}
}
:host ::ng-deep {
.mat-tab-body-wrapper {
min-height: 200px;
}
.mat-tab-body {
padding: 16px 0;
}
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

257
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts

@ -0,0 +1,257 @@
///
/// 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, Inject, OnDestroy, OnInit } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import {
DeviceCredentialsDialogLwm2mData,
getClientSecurityConfig,
JSON_ALL_CONFIG,
KEY_REGEXP_HEX_DEC,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
Lwm2mSecurityConfigModels,
Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap
} from '@shared/models/lwm2m-security-config.models';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { MatTab } from '@angular/material/tabs/tab';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'tb-security-config-lwm2m',
templateUrl: './security-config-lwm2m.component.html',
styleUrls: ['./security-config-lwm2m.component.scss']
})
export class SecurityConfigLwm2mComponent extends DialogComponent<SecurityConfigLwm2mComponent, object> implements OnInit, OnDestroy {
private destroy$ = new Subject();
lwm2mConfigFormGroup: FormGroup;
title: string;
securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.keys(Lwm2mSecurityType);
credentialTypeLwM2MNamesMap = Lwm2mSecurityTypeTranslationMap;
formControlNameJsonAllConfig = JSON_ALL_CONFIG;
jsonAllConfig: Lwm2mSecurityConfigModels;
lenMaxKeyClient = LEN_MAX_PSK;
tabPrevious: MatTab;
tabIndexPrevious = 0;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogLwm2mData,
public dialogRef: MatDialogRef<SecurityConfigLwm2mComponent, object>,
public fb: FormBuilder,
public translate: TranslateService) {
super(store, router, dialogRef);
}
ngOnInit() {
this.jsonAllConfig = JSON.parse(JSON.stringify(this.data.jsonAllConfig));
this.lwm2mConfigFormGroup = this.initLwm2mConfigFormGroup();
this.title = this.translate.instant('device.lwm2m-security-info') + ': ' + this.data.endPoint;
this.lwm2mConfigFormGroup.get('x509').disable();
this.initClientSecurityConfig(this.lwm2mConfigFormGroup.get('jsonAllConfig').value);
this.registerDisableOnLoadFormControl(this.lwm2mConfigFormGroup.get('securityConfigClientMode'));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private initClientSecurityConfig = (jsonAllConfig: Lwm2mSecurityConfigModels): void => {
if (jsonAllConfig.client.securityConfigClientMode !== Lwm2mSecurityType.NO_SEC) {
this.lwm2mConfigFormGroup.patchValue(jsonAllConfig.client, {emitEvent: false});
}
this.securityConfigClientUpdateValidators(jsonAllConfig.client.securityConfigClientMode);
}
private securityConfigClientModeChanged(type: Lwm2mSecurityType): void {
const config = getClientSecurityConfig(type, this.lwm2mConfigFormGroup.get('endPoint').value);
switch (type) {
case Lwm2mSecurityType.PSK:
config.identity = this.data.endPoint;
config.key = this.lwm2mConfigFormGroup.get('key').value;
break;
case Lwm2mSecurityType.RPK:
config.key = this.lwm2mConfigFormGroup.get('key').value;
break;
}
this.jsonAllConfig.client = config;
this.lwm2mConfigFormGroup.patchValue({
...config,
jsonAllConfig: this.jsonAllConfig
}, {emitEvent: false});
this.securityConfigClientUpdateValidators(type);
}
private securityConfigClientUpdateValidators = (mode: Lwm2mSecurityType): void => {
switch (mode) {
case Lwm2mSecurityType.NO_SEC:
case Lwm2mSecurityType.X509:
this.setValidatorsNoSecX509();
break;
case Lwm2mSecurityType.PSK:
this.lenMaxKeyClient = LEN_MAX_PSK;
this.setValidatorsPskRpk(mode);
break;
case Lwm2mSecurityType.RPK:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
this.setValidatorsPskRpk(mode);
break;
}
this.lwm2mConfigFormGroup.get('identity').updateValueAndValidity({emitEvent: false});
this.lwm2mConfigFormGroup.get('key').updateValueAndValidity({emitEvent: false});
}
private setValidatorsNoSecX509 = (): void => {
this.lwm2mConfigFormGroup.get('identity').setValidators([]);
this.lwm2mConfigFormGroup.get('key').setValidators([]);
}
private setValidatorsPskRpk = (mode: Lwm2mSecurityType): void => {
if (mode === Lwm2mSecurityType.PSK) {
this.lwm2mConfigFormGroup.get('identity').setValidators([Validators.required]);
} else {
this.lwm2mConfigFormGroup.get('identity').setValidators([]);
}
this.lwm2mConfigFormGroup.get('key').setValidators([Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.maxLength(this.lenMaxKeyClient), Validators.minLength(this.lenMaxKeyClient)]);
}
tabChanged = (tabChangeEvent: MatTabChangeEvent): void => {
if (this.tabIndexPrevious !== tabChangeEvent.index) {
this.upDateValueToJson();
}
this.tabIndexPrevious = tabChangeEvent.index;
}
private upDateValueToJson(): void {
switch (this.tabIndexPrevious) {
case 0:
this.upDateValueToJsonTab0();
break;
case 1:
this.upDateValueToJsonTab1();
break;
}
}
private upDateValueToJsonTab0 = (): void => {
if (this.lwm2mConfigFormGroup.get('identity').dirty && this.lwm2mConfigFormGroup.get('identity').valid ||
this.lwm2mConfigFormGroup.get('key').dirty && this.lwm2mConfigFormGroup.get('key').valid) {
this.updateBootstrapSettings();
this.upDateJsonAllConfig();
}
}
private upDateValueToJsonTab1 = (): void => {
const bootstrap = this.lwm2mConfigFormGroup.get('bootstrapServer').value;
if (bootstrap !== null
&& this.lwm2mConfigFormGroup.get('bootstrapServer').dirty
&& this.lwm2mConfigFormGroup.get('bootstrapServer').valid) {
this.jsonAllConfig.bootstrap.bootstrapServer = bootstrap;
this.upDateJsonAllConfig();
}
const serverConfig = this.lwm2mConfigFormGroup.get('lwm2mServer').value;
if (serverConfig !== null
&& this.lwm2mConfigFormGroup.get('lwm2mServer').dirty
&& this.lwm2mConfigFormGroup.get('lwm2mServer').valid) {
this.jsonAllConfig.bootstrap.lwm2mServer = serverConfig;
this.upDateJsonAllConfig();
}
}
private updateBootstrapSettings() {
const securityMode = 'securityMode';
this.jsonAllConfig.client.identity = this.lwm2mConfigFormGroup.get('identity').value;
this.jsonAllConfig.client.key = this.lwm2mConfigFormGroup.get('key').value;
if (this.lwm2mConfigFormGroup.get('bootstrapServer').value[securityMode] === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.bootstrap.bootstrapServer.clientPublicKeyOrId = this.jsonAllConfig.client.identity;
this.jsonAllConfig.bootstrap.bootstrapServer.clientSecretKey = this.jsonAllConfig.client.key;
this.lwm2mConfigFormGroup.get('bootstrapServer').patchValue(this.jsonAllConfig.bootstrap.bootstrapServer, {emitEvent: false});
}
if (this.lwm2mConfigFormGroup.get('lwm2mServer').value[securityMode] === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.bootstrap.lwm2mServer.clientPublicKeyOrId = this.jsonAllConfig.client.identity;
this.jsonAllConfig.bootstrap.lwm2mServer.clientSecretKey = this.jsonAllConfig.client.key;
this.lwm2mConfigFormGroup.get('lwm2mServer').patchValue(this.jsonAllConfig.bootstrap.lwm2mServer, {emitEvent: false});
}
}
private upDateJsonAllConfig = (): void => {
this.lwm2mConfigFormGroup.patchValue({
jsonAllConfig: this.jsonAllConfig
}, {emitEvent: false});
}
private initLwm2mConfigFormGroup = (): FormGroup => {
if (this.jsonAllConfig.client.securityConfigClientMode === Lwm2mSecurityType.PSK) {
this.data.endPoint = this.jsonAllConfig.client.endpoint;
}
const formGroup = this.fb.group({
securityConfigClientMode: [this.jsonAllConfig.client.securityConfigClientMode],
identity: [''],
key: [''],
x509: [false],
bootstrapServer: [this.jsonAllConfig.bootstrap.bootstrapServer],
lwm2mServer: [this.jsonAllConfig.bootstrap.lwm2mServer],
endPoint: [this.data.endPoint],
jsonAllConfig: [this.jsonAllConfig]
});
formGroup.get('securityConfigClientMode').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((type) => {
this.securityConfigClientModeChanged(type);
});
formGroup.get('endPoint').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((endpoint) => {
if (formGroup.get('securityConfigClientMode').value === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.client.endpoint = endpoint;
this.upDateJsonAllConfig();
}
});
return formGroup;
}
save(): void {
this.upDateValueToJson();
this.data.endPoint = this.lwm2mConfigFormGroup.get('endPoint').value.split('\'').join('');
this.data.jsonAllConfig = this.jsonAllConfig;
if (this.lwm2mConfigFormGroup.get('securityConfigClientMode').value === Lwm2mSecurityType.PSK) {
this.data.endPoint = this.data.jsonAllConfig.client.identity;
}
this.dialogRef.close(this.data);
}
cancel(): void {
this.dialogRef.close(undefined);
}
}

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

@ -138,6 +138,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb
import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-table.component'; import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-table.component';
import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component'; import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component';
import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component'; import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -239,6 +241,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent, DeviceWizardDialogComponent,
DeviceCredentialsComponent, DeviceCredentialsComponent,
CopyDeviceCredentialsComponent, CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleDialogComponent, AlarmScheduleDialogComponent,
EditAlarmDetailsDialogComponent, EditAlarmDetailsDialogComponent,
SmsProviderConfigurationComponent, SmsProviderConfigurationComponent,
@ -342,6 +346,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent, DeviceWizardDialogComponent,
DeviceCredentialsComponent, DeviceCredentialsComponent,
CopyDeviceCredentialsComponent, CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleInfoComponent, AlarmScheduleInfoComponent,
AlarmScheduleComponent, AlarmScheduleComponent,
AlarmScheduleDialogComponent, AlarmScheduleDialogComponent,

27
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts

@ -14,21 +14,20 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, Inject, OnInit, SkipSelf} from "@angular/core"; import { Component, Inject, OnInit, SkipSelf } from '@angular/core';
import {ErrorStateMatcher} from "@angular/material/core"; import { ErrorStateMatcher } from '@angular/material/core';
import {DialogComponent} from "@shared/components/dialog.component"; import { DialogComponent } from '@shared/components/dialog.component';
import {Store} from "@ngrx/store"; import { Store } from '@ngrx/store';
import {AppState} from "@core/core.state"; import { AppState } from '@core/core.state';
import {Router} from "@angular/router"; import { Router } from '@angular/router';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import {FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm} from "@angular/forms"; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
import {TranslateService} from "@ngx-translate/core"; import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point';
import {JsonObject} from "@angular/compiler-cli/ngcc/src/packages/entry_point";
export interface Lwm2mAttributesDialogData { export interface Lwm2mAttributesDialogData {
readonly: boolean; readonly: boolean;
attributeLwm2m: JsonObject; attributeLwm2m: JsonObject;
destName: string destName: string;
} }
@Component({ @Component({
@ -37,7 +36,8 @@ export interface Lwm2mAttributesDialogData {
styleUrls: ['./lwm2m-attributes.component.scss'], styleUrls: ['./lwm2m-attributes.component.scss'],
providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}],
}) })
export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttributesDialogComponent, Object> implements OnInit, ErrorStateMatcher { export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttributesDialogComponent, object>
implements OnInit, ErrorStateMatcher {
readonly = this.data.readonly; readonly = this.data.readonly;
@ -54,8 +54,7 @@ export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttribu
@Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData, @Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>, public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>,
private fb: FormBuilder, private fb: FormBuilder) {
public translate: TranslateService) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.attributeLwm2mDialogFormGroup = this.fb.group({ this.attributeLwm2mDialogFormGroup = this.fb.group({

30
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, forwardRef, Input, OnInit} from "@angular/core"; import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -26,17 +26,17 @@ import {
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
Validator, Validator,
Validators Validators
} from "@angular/forms"; } from '@angular/forms';
import {Subscription} from "rxjs"; import { Subscription } from 'rxjs';
import {PageComponent} from "@shared/components/page.component";
import {Store} from "@ngrx/store";
import {AppState} from "@core/core.state";
import { import {
ATTRIBUTE_KEYS, ATTRIBUTE_KEYS,
ATTRIBUTE_LWM2M_ENUM, ATTRIBUTE_LWM2M_ENUM,
ATTRIBUTE_LWM2M_MAP ATTRIBUTE_LWM2M_MAP
} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models"; } from './lwm2m-profile-config.models';
import {isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull} from "@core/utils"; import { isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull } from '@core/utils';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component';
@Component({ @Component({
@ -98,7 +98,7 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
registerOnTouched(fn: any): void { registerOnTouched(fn: any): void {
} }
setDisabledState?(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled;
if (this.disabled) { if (this.disabled) {
this.kvListFormGroup.disable({emitEvent: false}); this.kvListFormGroup.disable({emitEvent: false});
@ -166,10 +166,10 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
}; };
} }
private updateValidate (c?: FormControl) { private updateValidate() {
const kvList = this.kvListFormGroup.get('keyVals') as FormArray; const kvList = this.kvListFormGroup.get('keyVals') as FormArray;
kvList.controls.forEach(fg => { kvList.controls.forEach(fg => {
if (fg.get('key').value==='ver') { if (fg.get('key').value === 'ver') {
fg.get('value').setValidators(null); fg.get('value').setValidators(null);
fg.get('value').setErrors(null); fg.get('value').setErrors(null);
} }
@ -189,7 +189,7 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) { if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) {
keyValMap[entry.key] = entry.value.toString(); keyValMap[entry.key] = entry.value.toString();
} else { } else {
keyValMap[entry.key] = Number(entry.value) keyValMap[entry.key] = Number(entry.value);
} }
}); });
this.propagateChange(keyValMap); this.propagateChange(keyValMap);
@ -215,13 +215,13 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
private attributeLwm2mValueNumberValidator = (control: AbstractControl) => { private attributeLwm2mValueNumberValidator = (control: AbstractControl) => {
if (isNaN(Number(control.value)) || Number(control.value) < 0) { if (isNaN(Number(control.value)) || Number(control.value) < 0) {
return { return {
'validAttributeValue': true validAttributeValue: true
}; };
} }
return null; return null;
} }
private attributeLwm2mValueValidator = (property: string): Object [] => { private attributeLwm2mValueValidator = (property: string): object[] => {
return property === 'ver'? [] : [this.attributeLwm2mValueNumberValidator]; return property === 'ver' ? [] : [this.attributeLwm2mValueNumberValidator];
} }
} }

41
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts

@ -14,21 +14,14 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, EventEmitter, forwardRef, Inject, Input, Output} from "@angular/core"; import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from "@angular/forms"; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import {coerceBooleanProperty} from "@angular/cdk/coercion"; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {Store} from "@ngrx/store"; import { deepClone, isDefinedAndNotNull, isEmpty } from '@core/utils';
import {AppState} from "@core/core.state"; import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component';
import {DeviceProfileService} from "@core/http/device-profile.service"; import { MatDialog } from '@angular/material/dialog';
import {WINDOW} from "@core/services/window.service"; import { TranslateService } from '@ngx-translate/core';
import {deepClone, isDefinedAndNotNull, isEmpty} from "@core/utils"; import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models';
import {
Lwm2mAttributesDialogComponent,
Lwm2mAttributesDialogData
} from "@home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component";
import {MatDialog} from "@angular/material/dialog";
import {TranslateService} from "@ngx-translate/core";
import {ATTRIBUTE_LWM2M_LABEL} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models";
@Component({ @Component({
@ -46,7 +39,6 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL; attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL;
private requiredValue: boolean; private requiredValue: boolean;
private dirty = false;
@Input() @Input()
attributeLwm2m: {}; attributeLwm2m: {};
@ -71,12 +63,9 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
private propagateChange = (v: any) => { private propagateChange = (v: any) => {
} }
constructor(private store: Store<AppState>, constructor(private dialog: MatDialog,
private dialog: MatDialog,
private fb: FormBuilder, private fb: FormBuilder,
private deviceProfileService: DeviceProfileService, private translate: TranslateService) {}
private translate: TranslateService,
@Inject(WINDOW) private window: Window) {}
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
this.propagateChange = fn; this.propagateChange = fn;
@ -117,20 +106,20 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
return label; return label;
} }
isDisableBtn (): boolean { isDisableBtn(): boolean {
return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) && return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) &&
!isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled; !isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled;
} }
isIconView (): boolean { isIconView(): boolean {
return this.isAttributeTelemetry || this.disabled; return this.isAttributeTelemetry || this.disabled;
} }
isIconEditAdd (): boolean { isIconEditAdd(): boolean {
return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m); return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m);
} }
isToolTipLabel (): string { isToolTipLabel(): string {
return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') : return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') :
this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') : this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') :
this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip'); this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip');
@ -140,7 +129,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, Object>(Lwm2mAttributesDialogComponent, { this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, object>(Lwm2mAttributesDialogComponent, {
disableClose: true, disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {

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

@ -14,8 +14,8 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, forwardRef, Inject, Input} from '@angular/core'; import { Component, forwardRef, Inject, Input } from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { import {
DEFAULT_CLIENT_HOLD_OFF_TIME, DEFAULT_CLIENT_HOLD_OFF_TIME,
DEFAULT_ID_SERVER, DEFAULT_ID_SERVER,
@ -28,13 +28,12 @@ import {
SECURITY_CONFIG_MODE_NAMES, SECURITY_CONFIG_MODE_NAMES,
ServerSecurityConfig ServerSecurityConfig
} from './lwm2m-profile-config.models'; } from './lwm2m-profile-config.models';
import {Store} from '@ngrx/store'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {AppState} from '@core/core.state'; import { WINDOW } from '@core/services/window.service';
import {coerceBooleanProperty} from '@angular/cdk/coercion'; import { pairwise, startWith } from 'rxjs/operators';
import {WINDOW} from '@core/services/window.service'; import { DeviceProfileService } from '@core/http/device-profile.service';
import {pairwise, startWith} from 'rxjs/operators';
import {DeviceProfileService} from '@core/http/device-profile.service';
// @dynamic
@Component({ @Component({
selector: 'tb-profile-lwm2m-device-config-server', selector: 'tb-profile-lwm2m-device-config-server',
templateUrl: './lwm2m-device-config-server.component.html', templateUrl: './lwm2m-device-config-server.component.html',
@ -73,8 +72,7 @@ export class Lwm2mDeviceConfigServerComponent implements ControlValueAccessor {
this.requiredValue = coerceBooleanProperty(value); this.requiredValue = coerceBooleanProperty(value);
} }
constructor(protected store: Store<AppState>, constructor(public fb: FormBuilder,
public fb: FormBuilder,
private deviceProfileService: DeviceProfileService, private deviceProfileService: DeviceProfileService,
@Inject(WINDOW) private window: Window) { @Inject(WINDOW) private window: Window) {
this.serverFormGroup = this.initServerGroup(); this.serverFormGroup = this.initServerGroup();

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

@ -15,10 +15,8 @@
/// ///
import { DeviceProfileTransportConfiguration } from '@shared/models/device.models'; import { DeviceProfileTransportConfiguration } from '@shared/models/device.models';
import { Component, forwardRef, Inject, Input } from '@angular/core'; import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { import {
ATTRIBUTE, ATTRIBUTE,
@ -37,7 +35,6 @@ import {
} from './lwm2m-profile-config.models'; } from './lwm2m-profile-config.models';
import { DeviceProfileService } from '@core/http/device-profile.service'; import { DeviceProfileService } from '@core/http/device-profile.service';
import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/utils'; import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/utils';
import { WINDOW } from '@core/services/window.service';
import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
@ -78,10 +75,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private propagateChange = (v: any) => { private propagateChange = (v: any) => {
} }
constructor(private store: Store<AppState>, constructor(private fb: FormBuilder,
private fb: FormBuilder, private deviceProfileService: DeviceProfileService) {
private deviceProfileService: DeviceProfileService,
@Inject(WINDOW) private window: Window) {
this.lwm2mDeviceProfileFormGroup = this.fb.group({ this.lwm2mDeviceProfileFormGroup = this.fb.group({
clientOnlyObserveAfterConnect: [1, []], clientOnlyObserveAfterConnect: [1, []],
objectIds: [null, Validators.required], objectIds: [null, Validators.required],
@ -155,7 +150,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.lwm2mDeviceProfileFormGroup.patchValue({ this.lwm2mDeviceProfileFormGroup.patchValue({
clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect,
objectIds: value, objectIds: value,
observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value['objectsList']), observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList),
shortId: this.configurationValue.bootstrap.servers.shortId, shortId: this.configurationValue.bootstrap.servers.shortId,
lifetime: this.configurationValue.bootstrap.servers.lifetime, lifetime: this.configurationValue.bootstrap.servers.lifetime,
defaultMinPeriod: this.configurationValue.bootstrap.servers.defaultMinPeriod, defaultMinPeriod: this.configurationValue.bootstrap.servers.defaultMinPeriod,
@ -171,7 +166,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
let configuration: DeviceProfileTransportConfiguration = null; let configuration: DeviceProfileTransportConfiguration = null;
if (this.lwm2mDeviceConfigFormGroup.valid && this.lwm2mDeviceProfileFormGroup.valid) { if (this.lwm2mDeviceConfigFormGroup.valid && this.lwm2mDeviceProfileFormGroup.valid) {
configuration = this.lwm2mDeviceConfigFormGroup.value.configurationJson; configuration = this.lwm2mDeviceConfigFormGroup.value.configurationJson;
// configuration.type = DeviceTransportType.LWM2M;
} }
this.propagateChange(configuration); this.propagateChange(configuration);
} }
@ -206,7 +200,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) { if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) {
const attributeArray = this.configurationValue.observeAttr.attribute; const attributeArray = this.configurationValue.observeAttr.attribute;
const telemetryArray = this.configurationValue.observeAttr.telemetry; const telemetryArray = this.configurationValue.observeAttr.telemetry;
let keyNameJson = this.configurationValue.observeAttr.keyName; const keyNameJson = this.configurationValue.observeAttr.keyName;
if (this.includesNotZeroInstance(attributeArray, telemetryArray)) { if (this.includesNotZeroInstance(attributeArray, telemetryArray)) {
this.addInstances(attributeArray, telemetryArray, objectLwM2MS); this.addInstances(attributeArray, telemetryArray, objectLwM2MS);
} }
@ -270,9 +264,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private updateAttributeLwm2m = (objectLwM2MS: ObjectLwM2M[]): void => { private updateAttributeLwm2m = (objectLwM2MS: ObjectLwM2M[]): void => {
Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => { Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => {
const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String); const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
let objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId); const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId);
if (objectLwM2M && instanceId) { if (objectLwM2M && instanceId) {
let instance = objectLwM2M.instances.find(instance => instance.id === +instanceId) const instance = objectLwM2M.instances.find(obj => obj.id === +instanceId);
if (instance && resourceId) { if (instance && resourceId) {
instance.resources.find(resource => resource.id === +resourceId) instance.resources.find(resource => resource.id === +resourceId)
.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key]; .attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
@ -288,7 +282,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private updateKeyNameObjects = (objectLwM2MS: ObjectLwM2M[]): void => { private updateKeyNameObjects = (objectLwM2MS: ObjectLwM2M[]): void => {
Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => { Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => {
const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String); const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId) const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId);
if (objectLwM2M) { if (objectLwM2M) {
objectLwM2M.instances.find(instance => instance.id === +instanceId) objectLwM2M.instances.find(instance => instance.id === +instanceId)
.resources.find(resource => resource.id === +resourceId) .resources.find(resource => resource.id === +resourceId)
@ -299,7 +293,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => { private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => {
const keyName = JSON.parse(JSON.stringify(nameJson)); const keyName = JSON.parse(JSON.stringify(nameJson));
let keyNameValidate = {}; const keyNameValidate = {};
const keyAttrTelemetry = attributeArray.concat(telemetryArray); const keyAttrTelemetry = attributeArray.concat(telemetryArray);
Object.keys(keyName).forEach(key => { Object.keys(keyName).forEach(key => {
if (keyAttrTelemetry.includes(key)) { if (keyAttrTelemetry.includes(key)) {
@ -345,9 +339,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
attributeLwm2m[pathRes] = resource.attributeLwm2m; attributeLwm2m[pathRes] = resource.attributeLwm2m;
} }
} }
}) });
} }
}) });
} }
}); });
if (isUndefined(this.configurationValue.observeAttr)) { if (isUndefined(this.configurationValue.observeAttr)) {
@ -356,7 +350,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
attribute: attributeArray, attribute: attributeArray,
telemetry: telemetryArray, telemetry: telemetryArray,
keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew),
attributeLwm2m: attributeLwm2m attributeLwm2m
}; };
} else { } else {
this.configurationValue.observeAttr.observe = observeArray; this.configurationValue.observeAttr.observe = observeArray;
@ -457,7 +451,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
} }
private removeAttributeLwm2mFromJson = (keyId: string): void => { private removeAttributeLwm2mFromJson = (keyId: string): void => {
debugger
const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m; const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m;
Object.keys(keyNameJson).forEach(key => { Object.keys(keyNameJson).forEach(key => {
if (key.startsWith(`/${keyId}`)) { if (key.startsWith(`/${keyId}`)) {

14
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.ts

@ -14,13 +14,13 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, Inject, OnInit} from '@angular/core'; import { Component, Inject, OnInit } from '@angular/core';
import {DialogComponent} from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import {FormBuilder, FormGroup} from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import {Store} from '@ngrx/store'; import { Store } from '@ngrx/store';
import {AppState} from '@core/core.state'; import { AppState } from '@core/core.state';
import {Router} from '@angular/router'; import { Router } from '@angular/router';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
export interface Lwm2mObjectAddInstancesData { export interface Lwm2mObjectAddInstancesData {
instancesIds: Set<number>; instancesIds: Set<number>;

11
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts

@ -14,11 +14,9 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, forwardRef} from '@angular/core'; import { Component, forwardRef } from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {Store} from '@ngrx/store'; import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER } from './lwm2m-profile-config.models';
import {AppState} from '@core/core.state';
import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './lwm2m-profile-config.models';
@Component({ @Component({
selector: 'tb-profile-lwm2m-object-add-instances-list', selector: 'tb-profile-lwm2m-object-add-instances-list',
@ -41,8 +39,7 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private fb: FormBuilder) {
private fb: FormBuilder) {
this.instancesListFormGroup = this.fb.group({ this.instancesListFormGroup = this.fb.group({
instanceIdInput: [null, [ instanceIdInput: [null, [
Validators.min(this.instanceIdValueMin), Validators.min(this.instanceIdValueMin),

47
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts

@ -14,18 +14,16 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild} from '@angular/core'; import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {coerceBooleanProperty} from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {Store} from '@ngrx/store'; import { Observable } from 'rxjs';
import {AppState} from '@core/core.state'; import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
import {Observable} from 'rxjs'; import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models';
import {filter, map, mergeMap, publishReplay, refCount, tap} from 'rxjs/operators'; import { DeviceProfileService } from '@core/http/device-profile.service';
import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './lwm2m-profile-config.models'; import { Direction } from '@shared/models/page/sort-order';
import {DeviceProfileService} from '@core/http/device-profile.service'; import { isDefined, isDefinedAndNotNull, isString } from '@core/utils';
import {Direction} from '@shared/models/page/sort-order'; import { PageLink } from '@shared/models/page/page-link';
import {isDefined, isDefinedAndNotNull, isString} from '@core/utils';
import {PageLink} from "@shared/models/page/page-link";
@Component({ @Component({
selector: 'tb-profile-lwm2m-object-list', selector: 'tb-profile-lwm2m-object-list',
@ -71,8 +69,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
private propagateChange = (v: any) => { private propagateChange = (v: any) => {
} }
constructor(private store: Store<AppState>, constructor(private deviceProfileService: DeviceProfileService,
private deviceProfileService: DeviceProfileService,
private fb: FormBuilder) { private fb: FormBuilder) {
this.lwm2mListFormGroup = this.fb.group({ this.lwm2mListFormGroup = this.fb.group({
objectsList: [this.objectsList], objectsList: [this.objectsList],
@ -162,20 +159,20 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => { private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => {
this.searchText = searchText; this.searchText = searchText;
return this.getLwM2mModelsPage().pipe( return this.getLwM2mModelsPage().pipe(
map(objectLwM2Ms => objectLwM2Ms) map(objectLwM2Ms => objectLwM2Ms)
); );
} }
private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> { private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> {
const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, { const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, {
property: 'id', property: 'id',
direction: Direction.ASC direction: Direction.ASC
}); });
this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe( this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe(
publishReplay(1), publishReplay(1),
refCount() refCount()
); );
return this.lw2mModels; return this.lw2mModels;
} }

19
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts

@ -14,13 +14,11 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, forwardRef, Input} from '@angular/core'; import { Component, forwardRef, Input } from '@angular/core';
import {ControlValueAccessor, FormArray, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; import { ControlValueAccessor, FormArray, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {ResourceLwM2M, RESOURCES} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; import { ResourceLwM2M, RESOURCES } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import _ from 'lodash'; import _ from 'lodash';
import {coerceBooleanProperty} from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
@Component({ @Component({
selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource', selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource',
@ -54,8 +52,7 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
} }
} }
constructor(private store: Store<AppState>, constructor(private fb: FormBuilder) {
private fb: FormBuilder) {
this.resourceFormGroup = this.fb.group({ this.resourceFormGroup = this.fb.group({
resources: this.fb.array([]) resources: this.fb.array([])
}); });
@ -95,7 +92,7 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
} }
getNameResourceLwm2m = (resourceLwM2M: ResourceLwM2M): string => { getNameResourceLwm2m = (resourceLwM2M: ResourceLwM2M): string => {
return '<' + resourceLwM2M.id +'> ' + resourceLwM2M.name; return `<${resourceLwM2M.id}> ${resourceLwM2M.name}`;
} }
createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void { createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void {
@ -135,14 +132,14 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
return index; return index;
} }
updateObserve = (index: number): void =>{ updateObserve = (index: number): void => {
if (this.resourceFormArray.at(index).value.attribute === false && this.resourceFormArray.at(index).value.telemetry === false) { if (this.resourceFormArray.at(index).value.attribute === false && this.resourceFormArray.at(index).value.telemetry === false) {
this.resourceFormArray.at(index).patchValue({observe: false}); this.resourceFormArray.at(index).patchValue({observe: false});
this.resourceFormArray.at(index).patchValue({attributeLwm2m: {}}); this.resourceFormArray.at(index).patchValue({attributeLwm2m: {}});
} }
} }
disableObserve = (index: number): boolean =>{ disableObserve = (index: number): boolean => {
return !this.resourceFormArray.at(index).value.telemetry && !this.resourceFormArray.at(index).value.attribute; return !this.resourceFormArray.at(index).value.telemetry && !this.resourceFormArray.at(index).value.attribute;
} }
} }

24
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, forwardRef, Input} from '@angular/core'; import { Component, forwardRef, Input } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -24,9 +24,7 @@ import {
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import {Store} from '@ngrx/store'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {AppState} from '@core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import { import {
ATTRIBUTE, ATTRIBUTE,
ATTRIBUTE_LWM2M, ATTRIBUTE_LWM2M,
@ -38,9 +36,8 @@ import {
RESOURCES, RESOURCES,
TELEMETRY TELEMETRY
} from './lwm2m-profile-config.models'; } from './lwm2m-profile-config.models';
import {deepClone, isDefinedAndNotNull, isEqual, isUndefined} from '@core/utils'; import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils';
import {MatDialog} from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import {TranslateService} from '@ngx-translate/core';
import { import {
Lwm2mObjectAddInstancesData, Lwm2mObjectAddInstancesData,
Lwm2mObjectAddInstancesDialogComponent Lwm2mObjectAddInstancesDialogComponent
@ -83,10 +80,8 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
@Input() @Input()
disabled: boolean; disabled: boolean;
constructor(private store: Store<AppState>, constructor(private fb: FormBuilder,
private fb: FormBuilder, private dialog: MatDialog) {
private dialog: MatDialog,
public translate: TranslateService) {
this.observeAttrTelemetryFormGroup = this.fb.group({ this.observeAttrTelemetryFormGroup = this.fb.group({
[CLIENT_LWM2M]: this.fb.array([]) [CLIENT_LWM2M]: this.fb.array([])
}); });
@ -98,7 +93,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
} }
private propagateChange = (v: any) => { private propagateChange = (v: any) => {
}; }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
this.propagateChange = fn; this.propagateChange = fn;
@ -189,7 +184,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).updateValueAndValidity(); this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).updateValueAndValidity();
} }
trackByParams = (index: number, element: any): number => { trackByParams = (index: number): number => {
return index; return index;
} }
@ -252,7 +247,6 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
} }
private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => { private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => {
debugger
const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
.find(e => e.value.keyId === data.objectKeyId) as FormGroup; .find(e => e.value.keyId === data.objectKeyId) as FormGroup;
const instancesArray = objectLwM2MFormGroup.value.instances as Instance []; const instancesArray = objectLwM2MFormGroup.value.instances as Instance [];
@ -318,7 +312,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
return objectName + ' <' + idVerObj + '>'; return objectName + ' <' + idVerObj + '>';
} }
getNameInstanceLwm2m = (instance: Instance, idVerObj: string): string => { getNameInstanceLwm2m = (instance: Instance, idVerObj: string): string => {
return ' instance <' + idVerObj + '/' + instance.id +'>'; return ` instance <${idVerObj}/${instance.id}>`;
} }
updateAttributeLwm2mObject = (event: Event, objectKeyId: number): void => { updateAttributeLwm2mObject = (event: Event, objectKeyId: number): void => {

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

@ -14,19 +14,19 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {NgModule} from '@angular/core'; import { NgModule } from '@angular/core';
import {Lwm2mDeviceProfileTransportConfigurationComponent} from './lwm2m-device-profile-transport-configuration.component'; import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component';
import {Lwm2mObjectListComponent} from './lwm2m-object-list.component'; import { Lwm2mObjectListComponent } from './lwm2m-object-list.component';
import {Lwm2mObserveAttrTelemetryComponent} from './lwm2m-observe-attr-telemetry.component'; import { Lwm2mObserveAttrTelemetryComponent } from './lwm2m-observe-attr-telemetry.component';
import {Lwm2mObserveAttrTelemetryResourceComponent} from './lwm2m-observe-attr-telemetry-resource.component'; import { Lwm2mObserveAttrTelemetryResourceComponent } from './lwm2m-observe-attr-telemetry-resource.component';
import {Lwm2mAttributesDialogComponent} from './lwm2m-attributes-dialog.component'; import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component';
import {Lwm2mAttributesComponent} from './lwm2m-attributes.component'; import { Lwm2mAttributesComponent } from './lwm2m-attributes.component';
import {Lwm2mAttributesKeyListComponent} from './lwm2m-attributes-key-list.component'; import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component';
import {Lwm2mDeviceConfigServerComponent} from './lwm2m-device-config-server.component'; import { Lwm2mDeviceConfigServerComponent } from './lwm2m-device-config-server.component';
import {Lwm2mObjectAddInstancesDialogComponent} from './lwm2m-object-add-instances-dialog.component'; import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-instances-dialog.component';
import {Lwm2mObjectAddInstancesListComponent} from './lwm2m-object-add-instances-list.component'; import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component';
import {CommonModule} from '@angular/common'; import { CommonModule } from '@angular/common';
import {SharedModule} from '@app/shared/shared.module'; import { SharedModule } from '@app/shared/shared.module';
@NgModule({ @NgModule({
declarations: declarations:

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

@ -127,8 +127,8 @@ export const SECURITY_CONFIG_MODE_NAMES = new Map<SECURITY_CONFIG_MODE, string>(
); );
export interface ModelValue { export interface ModelValue {
objectIds: string[], objectIds: string[];
objectsList: ObjectLwM2M[] objectsList: ObjectLwM2M[];
} }
export interface BootstrapServersSecurityConfig { export interface BootstrapServersSecurityConfig {

6
ui-ngx/src/app/modules/home/pages/device/device.module.ts

@ -24,8 +24,6 @@ import { DeviceCredentialsDialogComponent } from '@modules/home/pages/device/dev
import { HomeDialogsModule } from '../../dialogs/home-dialogs.module'; import { HomeDialogsModule } from '../../dialogs/home-dialogs.module';
import { HomeComponentsModule } from '@modules/home/components/home-components.module'; import { HomeComponentsModule } from '@modules/home/components/home-components.module';
import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component'; import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component';
import { SecurityConfigComponent } from '@home/pages/device/lwm2m/security-config.component';
import { SecurityConfigServerComponent } from '@home/pages/device/lwm2m/security-config-server.component';
import { DefaultDeviceConfigurationComponent } from './data/default-device-configuration.component'; import { DefaultDeviceConfigurationComponent } from './data/default-device-configuration.component';
import { DeviceConfigurationComponent } from './data/device-configuration.component'; import { DeviceConfigurationComponent } from './data/device-configuration.component';
import { DeviceDataComponent } from './data/device-data.component'; import { DeviceDataComponent } from './data/device-data.component';
@ -50,9 +48,7 @@ import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-tr
DeviceComponent, DeviceComponent,
DeviceTabsComponent, DeviceTabsComponent,
DeviceTableHeaderComponent, DeviceTableHeaderComponent,
DeviceCredentialsDialogComponent, DeviceCredentialsDialogComponent
SecurityConfigComponent,
SecurityConfigServerComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

101
ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.html

@ -1,101 +0,0 @@
<!--
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.
-->
<section [formGroup]="serverFormGroup" style="min-width: 400px;">
<div class="mat-padding">
<div fxLayout="column">
<div fxLayout="row" fxLayoutGap="8px">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityMode"
(ngModelChange)="securityModeChanged($event)">
<mat-option *ngFor="let securityMode of securityConfigLwM2MTypes"
[value]="securityMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div fxLayout="column">
<div [fxShow]="serverFormGroup.get('securityMode').value !== securityConfigLwM2MType.NO_SEC">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}</mat-label>
<textarea matInput
#clientPublicKeyOrId
maxlength={{lenMaxClientPublicKeyOrId}}
cdkTextareaAutosize
cdkAutosizeMinRows="1"
style="overflow:hidden"
cols="1"
formControlName="clientPublicKeyOrId"
[required]="serverFormGroup.get('securityMode').value !== securityConfigLwM2MType.NO_SEC">
</textarea>
<mat-hint align="end">{{clientPublicKeyOrId.value?.length || 0}}/{{lenMaxClientPublicKeyOrId}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('required')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}
<strong>{{ 'device.lwm2m-security-config.required' | translate }}</strong>
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.pattern_hex_dec' | translate: {
count: 0
} }}</strong>
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientPublicKeyOrId').hasError('maxlength') ||
serverFormGroup.get('clientPublicKeyOrId').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.pattern_hex_dec' | translate: {
count: lenMaxClientPublicKeyOrId
} }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-secret-key' | translate }}</mat-label>
<textarea matInput
#clientSecretKey
maxlength={{lenMaxClientSecretKey}}
cdkTextareaAutosize
cdkAutosizeMinRows="1"
style="overflow:hidden"
cols="1"
formControlName="clientSecretKey"
[required]="serverFormGroup.get('securityMode').value !== securityConfigLwM2MType.NO_SEC">
</textarea>
<mat-hint align="end">{{clientSecretKey.value?.length || 0}}/{{lenMaxClientSecretKey}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('required')">
{{ 'device.lwm2m-security-config.client-secret-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.required' | translate }}</strong>
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.pattern_hex_dec' | translate: {
count: 0
} }}</strong>
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientSecretKey').hasError('maxlength') ||
serverFormGroup.get('clientSecretKey').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.pattern_hex_dec' | translate: {
count: lenMaxClientSecretKey
} }}</strong>
</mat-error>
</mat-form-field>
</div>
</div>
</div>
</section>

142
ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config-server.component.ts

@ -1,142 +0,0 @@
///
/// 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, Inject, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
DeviceCredentialsDialogLwm2mData,
KEY_REGEXP_HEX_DEC,
LEN_MAX_PRIVATE_KEY,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
LEN_MAX_PUBLIC_KEY_X509,
SECURITY_CONFIG_MODE,
SECURITY_CONFIG_MODE_NAMES,
ServerSecurityConfig
} from '@home/pages/device/lwm2m/security-config.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { PageComponent } from '@shared/components/page.component';
@Component({
selector: 'tb-security-config-server-lwm2m',
templateUrl: './security-config-server.component.html',
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SecurityConfigServerComponent),
multi: true
}
]
})
export class SecurityConfigServerComponent extends PageComponent implements OnInit, ControlValueAccessor {
securityConfigLwM2MType = SECURITY_CONFIG_MODE;
securityConfigLwM2MTypes = Object.keys(SECURITY_CONFIG_MODE);
credentialTypeLwM2MNamesMap = SECURITY_CONFIG_MODE_NAMES;
lenMinClientPublicKeyOrId = 0;
lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
lenMinClientSecretKey = LEN_MAX_PRIVATE_KEY;
lenMaxClientSecretKey = LEN_MAX_PRIVATE_KEY;
@Input() serverFormGroup: FormGroup;
constructor(protected store: Store<AppState>,
@Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogLwm2mData,
public dialogRef: MatDialogRef<SecurityConfigServerComponent, object>,
public fb: FormBuilder) {
super(store);
}
ngOnInit(): void {
this.registerDisableOnLoadFormControl(this.serverFormGroup.get('securityMode'));
}
private updateValueFields(serverData: ServerSecurityConfig): void {
this.serverFormGroup.patchValue(serverData, {emitEvent: false});
const securityMode = this.serverFormGroup.get('securityMode').value as SECURITY_CONFIG_MODE;
this.updateValidate(securityMode);
}
private updateValidate(securityMode: SECURITY_CONFIG_MODE): void {
switch (securityMode) {
case SECURITY_CONFIG_MODE.NO_SEC:
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([]);
this.serverFormGroup.get('clientSecretKey').setValidators([]);
break;
case SECURITY_CONFIG_MODE.PSK:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lenMinClientSecretKey = LEN_MAX_PSK;
this.lenMaxClientSecretKey = LEN_MAX_PSK;
this.setValidatorsSecurity(securityMode);
break;
case SECURITY_CONFIG_MODE.RPK:
this.lenMinClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lenMinClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.lenMaxClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
case SECURITY_CONFIG_MODE.X509:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_X509;
this.lenMinClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.lenMaxClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
}
this.serverFormGroup.updateValueAndValidity();
}
private setValidatorsSecurity = (securityMode: SECURITY_CONFIG_MODE): void => {
if (securityMode === SECURITY_CONFIG_MODE.PSK) {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([Validators.required]);
} else {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lenMinClientPublicKeyOrId),
Validators.maxLength(this.lenMaxClientPublicKeyOrId)]);
}
this.serverFormGroup.get('clientSecretKey').setValidators([Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lenMinClientSecretKey),
Validators.maxLength(this.lenMaxClientSecretKey)]);
}
securityModeChanged(securityMode: SECURITY_CONFIG_MODE): void {
this.updateValidate(securityMode);
}
writeValue(value: any): void {
if (value) {
this.updateValueFields(value);
}
}
registerOnChange(fn: (value: any) => any): void {
}
registerOnTouched(fn: any): void {
}
setDisabledState?(isDisabled: boolean): void {
}
}

180
ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.html

@ -1,180 +0,0 @@
<!--
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]="lwm2mConfigFormGroup" (ngSubmit)="save()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary">
<h2>{{ 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>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.endpoint</mat-label>
<input matInput type="text" formControlName="endPoint" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('endPoint').hasError('required')">
{{ 'device.lwm2m-security-config.endpoint' | translate }}
<strong>{{ 'device.lwm2m-security-config.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-tab-group dynamicHeight (selectedTabChange)="tabChanged($event)">
<mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}">
<ng-template matTabContent>
<div class="mat-padding">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityConfigClientMode"
(ngModelChange)="securityConfigClientModeChanged($event)">
<mat-option *ngFor="let securityConfigClientMode of securityConfigLwM2MTypes"
[value]="securityConfigClientMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityConfigClientMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<div *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.identity' | translate }}</mat-label>
<input matInput type="text" formControlName="identityPSK"
[required]="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-error *ngIf="lwm2mConfigFormGroup.get('identityPSK').hasError('required')">
{{ 'device.lwm2m-security-config.identity' | translate }}
<strong>{{ 'device.lwm2m-security-config.required' | translate }}</strong>
</mat-error>
</mat-form-field>
</div>
<div *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.RPK ||
lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-key' | translate }}</mat-label>
<textarea matInput
#clientKey
maxlength="{{lenMaxKeyClient}}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="clientKey"
style="overflow:hidden"
[required]="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.RPK ||
lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
</textarea>
<mat-hint align="end">{{clientKey.value?.length || 0}}/{{lenMaxKeyClient}}</mat-hint>
<mat-error *ngIf="lwm2mConfigFormGroup.get('clientKey').hasError('required')">
{{ 'device.lwm2m-security-config.client-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.required' | translate }}</strong>
</mat-error>
<mat-error *ngIf="lwm2mConfigFormGroup.get('clientKey').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.pattern_hex_dec' | translate: {
count: 0
} }}</strong>
</mat-error>
<mat-error *ngIf="(lwm2mConfigFormGroup.get('clientKey').hasError('maxlength') ||
lwm2mConfigFormGroup.get('clientKey').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-key' | translate }}
<strong>{{ 'device.lwm2m-security-config.pattern_hex_dec' | translate: {
count: lenMaxKeyClient
} }}</strong>
</mat-error>
</mat-form-field>
</div>
<div *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.X509">
<mat-checkbox formControlName="clientCertificate" color="primary">
{{ 'device.lwm2m-security-config.client-certificate' | translate }}
</mat-checkbox>
</div>
</div>
</ng-template>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}">
<ng-template matTabContent>
<div class="mat-padding">
<mat-accordion multi="true" class="mat-body-1">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<div
class="tb-panel-title">{{ 'device.lwm2m-security-config.bootstrap-server' | translate | uppercase }}</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div class="mat-padding">
<tb-security-config-server-lwm2m
[formControlName]="bootstrapServer"
[serverFormGroup]="bootstrapFormGroup">
</tb-security-config-server-lwm2m>
</div>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
<mat-accordion multi="true" class="mat-body-1">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<div
class="tb-panel-title">{{ 'device.lwm2m-security-config.lwm2m-server' | translate | uppercase }}</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div class="mat-padding">
<tb-security-config-server-lwm2m
[formControlName]="lwm2mServer"
[serverFormGroup]="lwm2mServerFormGroup">
</tb-security-config-server-lwm2m>
</div>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
</div>
</ng-template>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.config-json-tab' | translate }}">
<ng-template matTabContent>
<div class="mat-padding">
<fieldset [disabled]="isLoading$ | async">
<tb-json-object-edit
[formControlName]="formControlNameJsonAllConfig"
label="{{ 'device.lwm2m-value' | translate }}"
validateContent="true"
[required]="true"
[fillHeight]="false">
</tb-json-object-edit>
</fieldset>
</div>
</ng-template>
</mat-tab>
</mat-tab-group>
</fieldset>
</div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<span fxFlex></span>
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || lwm2mConfigFormGroup.invalid || !lwm2mConfigFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div>
</form>

382
ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.component.ts

@ -1,382 +0,0 @@
///
/// 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, Inject, OnInit} from '@angular/core';
import {DialogComponent} from '@shared/components/dialog.component';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {Router} from '@angular/router';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {TranslateService} from '@ngx-translate/core';
import {
BOOTSTRAP_SERVER,
BOOTSTRAP_SERVERS,
ClientSecurityConfig,
DeviceCredentialsDialogLwm2mData,
getClientSecurityConfig,
JSON_ALL_CONFIG,
KEY_REGEXP_HEX_DEC,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
LWM2M_SERVER,
SECURITY_CONFIG_MODE,
SECURITY_CONFIG_MODE_NAMES,
SecurityConfigModels
} from './security-config.models';
import {WINDOW} from '@core/services/window.service';
import {MatTabChangeEvent} from '@angular/material/tabs';
import {MatTab} from '@angular/material/tabs/tab';
@Component({
selector: 'tb-security-config-lwm2m',
templateUrl: './security-config.component.html',
styleUrls: []
})
export class SecurityConfigComponent extends DialogComponent<SecurityConfigComponent, object> implements OnInit {
lwm2mConfigFormGroup: FormGroup;
title: string;
submitted = false;
securityConfigLwM2MType = SECURITY_CONFIG_MODE;
securityConfigLwM2MTypes = Object.keys(SECURITY_CONFIG_MODE);
credentialTypeLwM2MNamesMap = SECURITY_CONFIG_MODE_NAMES;
formControlNameJsonAllConfig = JSON_ALL_CONFIG;
jsonAllConfig: SecurityConfigModels;
bootstrapServers: string;
bootstrapServer: string;
lwm2mServer: string;
jsonObserveData: {};
lenMaxKeyClient = LEN_MAX_PSK;
tabPrevious: MatTab;
tabIndexPrevious = 0;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogLwm2mData,
public dialogRef: MatDialogRef<SecurityConfigComponent, object>,
public fb: FormBuilder,
public translate: TranslateService,
@Inject(WINDOW) private window: Window) {
super(store, router, dialogRef);
}
ngOnInit(): void {
this.jsonAllConfig = JSON.parse(JSON.stringify(this.data.jsonAllConfig)) as SecurityConfigModels;
this.initConstants();
this.lwm2mConfigFormGroup = this.initLwm2mConfigFormGroup();
this.title = this.translate.instant('device.lwm2m-security-info') + ': ' + this.data.endPoint;
this.lwm2mConfigFormGroup.get('clientCertificate').disable();
this.initClientSecurityConfig(this.lwm2mConfigFormGroup.get('jsonAllConfig').value);
this.registerDisableOnLoadFormControl(this.lwm2mConfigFormGroup.get('securityConfigClientMode'));
}
private initConstants = (): void => {
this.bootstrapServers = BOOTSTRAP_SERVERS;
this.bootstrapServer = BOOTSTRAP_SERVER;
this.lwm2mServer = LWM2M_SERVER;
}
/**
* initChildesFormGroup
*/
get bootstrapFormGroup(): FormGroup {
return this.lwm2mConfigFormGroup.get('bootstrapFormGroup') as FormGroup;
}
get lwm2mServerFormGroup(): FormGroup {
return this.lwm2mConfigFormGroup.get('lwm2mServerFormGroup') as FormGroup;
}
get observeAttrFormGroup(): FormGroup {
return this.lwm2mConfigFormGroup.get('observeFormGroup') as FormGroup;
}
private initClientSecurityConfig = (jsonAllConfig: SecurityConfigModels): void => {
switch (jsonAllConfig.client.securityConfigClientMode) {
case SECURITY_CONFIG_MODE.NO_SEC:
break;
case SECURITY_CONFIG_MODE.PSK:
const clientSecurityConfigPSK = jsonAllConfig.client as ClientSecurityConfig;
this.lwm2mConfigFormGroup.patchValue({
identityPSK: clientSecurityConfigPSK.identity,
clientKey: clientSecurityConfigPSK.key,
}, {emitEvent: false});
break;
case SECURITY_CONFIG_MODE.RPK:
const clientSecurityConfigRPK = jsonAllConfig.client as ClientSecurityConfig;
this.lwm2mConfigFormGroup.patchValue({
clientKey: clientSecurityConfigRPK.key,
}, {emitEvent: false});
break;
case SECURITY_CONFIG_MODE.X509:
const clientSecurityConfigX509 = jsonAllConfig.client as ClientSecurityConfig;
this.lwm2mConfigFormGroup.patchValue({
clientCertificate: clientSecurityConfigX509.x509
}, {emitEvent: false});
break;
}
this.securityConfigClientUpdateValidators(this.lwm2mConfigFormGroup.get('securityConfigClientMode').value);
}
securityConfigClientModeChanged = (mode: SECURITY_CONFIG_MODE): void => {
switch (mode) {
case SECURITY_CONFIG_MODE.NO_SEC:
const clientSecurityConfigNoSEC = getClientSecurityConfig(mode) as ClientSecurityConfig;
this.jsonAllConfig.client = clientSecurityConfigNoSEC;
this.lwm2mConfigFormGroup.patchValue({
jsonAllConfig: this.jsonAllConfig,
clientCertificate: false
}, {emitEvent: false});
break;
case SECURITY_CONFIG_MODE.PSK:
const clientSecurityConfigPSK = getClientSecurityConfig(mode, this.lwm2mConfigFormGroup.get('endPoint')
.value) as ClientSecurityConfig;
clientSecurityConfigPSK.identity = this.data.endPoint;
clientSecurityConfigPSK.key = this.lwm2mConfigFormGroup.get('clientKey').value;
this.jsonAllConfig.client = clientSecurityConfigPSK;
this.lwm2mConfigFormGroup.patchValue({
identityPSK: clientSecurityConfigPSK.identity,
clientCertificate: false
}, {emitEvent: false});
break;
case SECURITY_CONFIG_MODE.RPK:
const clientSecurityConfigRPK = getClientSecurityConfig(mode) as ClientSecurityConfig;
clientSecurityConfigRPK.key = this.lwm2mConfigFormGroup.get('clientKey').value;
this.jsonAllConfig.client = clientSecurityConfigRPK;
this.lwm2mConfigFormGroup.patchValue({
clientCertificate: false
}, {emitEvent: false});
break;
case SECURITY_CONFIG_MODE.X509:
this.jsonAllConfig.client = getClientSecurityConfig(mode) as ClientSecurityConfig;
this.lwm2mConfigFormGroup.patchValue({
clientCertificate: true
}, {emitEvent: false});
break;
}
this.securityConfigClientUpdateValidators(mode);
}
private securityConfigClientUpdateValidators = (mode: SECURITY_CONFIG_MODE): void => {
switch (mode) {
case SECURITY_CONFIG_MODE.NO_SEC:
this.setValidatorsNoSecX509();
break;
case SECURITY_CONFIG_MODE.PSK:
this.lenMaxKeyClient = LEN_MAX_PSK;
this.setValidatorsPskRpk(mode);
break;
case SECURITY_CONFIG_MODE.RPK:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
this.setValidatorsPskRpk(mode);
break;
case SECURITY_CONFIG_MODE.X509:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
this.setValidatorsNoSecX509();
break;
}
this.lwm2mConfigFormGroup.updateValueAndValidity();
}
private setValidatorsNoSecX509 = (): void => {
this.lwm2mConfigFormGroup.get('identityPSK').setValidators([]);
this.lwm2mConfigFormGroup.get('clientKey').setValidators([]);
}
private setValidatorsPskRpk = (mode: SECURITY_CONFIG_MODE): void => {
if (mode === SECURITY_CONFIG_MODE.PSK) {
this.lwm2mConfigFormGroup.get('identityPSK').setValidators([Validators.required]);
} else {
this.lwm2mConfigFormGroup.get('identityPSK').setValidators([]);
}
this.lwm2mConfigFormGroup.get('clientKey').setValidators([Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.maxLength(this.lenMaxKeyClient), Validators.minLength(this.lenMaxKeyClient)]);
}
tabChanged = (tabChangeEvent: MatTabChangeEvent): void => {
if (this.tabIndexPrevious !== tabChangeEvent.index) { this.upDateValueToJson(); }
this.tabIndexPrevious = tabChangeEvent.index;
}
private upDateValueToJson(): void {
switch (this.tabIndexPrevious) {
case 0:
this.upDateValueToJsonTab0();
break;
case 1:
this.upDateValueToJsonTab1();
break;
case 2:
this.upDateValueToJsonTab2();
break;
}
}
private upDateValueToJsonTab0 = (): void => {
if (this.lwm2mConfigFormGroup !== null) {
if (!this.lwm2mConfigFormGroup.get('endPoint').pristine && this.lwm2mConfigFormGroup.get('endPoint').valid) {
this.data.endPoint = this.lwm2mConfigFormGroup.get('endPoint').value;
/** Client mode == PSK */
if (this.lwm2mConfigFormGroup.get('securityConfigClientMode').value === SECURITY_CONFIG_MODE.PSK) {
const endPoint = 'endpoint';
this.jsonAllConfig.client[endPoint] = this.data.endPoint;
this.lwm2mConfigFormGroup.get('endPoint').markAsPristine({
onlySelf: true
});
this.upDateJsonAllConfig();
}
}
/** only Client mode == PSK */
if (!this.lwm2mConfigFormGroup.get('identityPSK').pristine && this.lwm2mConfigFormGroup.get('identityPSK').valid) {
this.lwm2mConfigFormGroup.get('identityPSK').markAsPristine({
onlySelf: true
});
this.updateIdentityPSK();
}
/** only Client mode == PSK (len = 64) || RPK (len = 182) */
if (!this.lwm2mConfigFormGroup.get('clientKey').pristine && this.lwm2mConfigFormGroup.get('clientKey').valid) {
this.lwm2mConfigFormGroup.get('clientKey').markAsPristine({
onlySelf: true
});
this.updateClientKey();
}
}
}
private upDateValueToJsonTab1 = (): void => {
if (this.lwm2mConfigFormGroup !== null) {
if (this.bootstrapFormGroup !== null && !this.bootstrapFormGroup.pristine && this.bootstrapFormGroup.valid) {
this.jsonAllConfig.bootstrap.bootstrapServer = this.bootstrapFormGroup.value;
this.bootstrapFormGroup.markAsPristine({
onlySelf: true
});
this.upDateJsonAllConfig();
}
if (this.lwm2mServerFormGroup !== null && !this.lwm2mServerFormGroup.pristine && this.lwm2mServerFormGroup.valid) {
this.jsonAllConfig.bootstrap.lwm2mServer = this.lwm2mServerFormGroup.value;
this.lwm2mServerFormGroup.markAsPristine({
onlySelf: true
});
this.upDateJsonAllConfig();
}
}
}
private upDateValueToJsonTab2 = (): void => {
if (!this.lwm2mConfigFormGroup.get(this.formControlNameJsonAllConfig).pristine &&
this.lwm2mConfigFormGroup.get(this.formControlNameJsonAllConfig).valid) {
this.jsonAllConfig = this.lwm2mConfigFormGroup.get(this.formControlNameJsonAllConfig).value;
this.lwm2mConfigFormGroup.get(this.formControlNameJsonAllConfig).markAsPristine({
onlySelf: true
});
}
}
private updateIdentityPSK = (): void => {
const securityMode = 'securityMode';
if (this.lwm2mConfigFormGroup.get('bootstrapServer').value[securityMode] === SECURITY_CONFIG_MODE.PSK) {
this.lwm2mConfigFormGroup.get('bootstrapFormGroup').patchValue({
clientPublicKeyOrId: this.lwm2mConfigFormGroup.get('identityPSK').value
});
const identity = 'identity';
this.jsonAllConfig.client[identity] = this.lwm2mConfigFormGroup.get('identityPSK').value;
this.upDateJsonAllConfig();
}
if (this.lwm2mConfigFormGroup.get('lwm2mServer').value[securityMode] === SECURITY_CONFIG_MODE.PSK) {
this.lwm2mConfigFormGroup.get('lwm2mServerFormGroup').patchValue({
clientPublicKeyOrId: this.lwm2mConfigFormGroup.get('identityPSK').value
});
this.jsonAllConfig.bootstrap.lwm2mServer.clientPublicKeyOrId = this.lwm2mConfigFormGroup.get('identityPSK').value;
this.upDateJsonAllConfig();
}
}
private updateClientKey = (): void => {
const key = 'key';
const securityMode = 'securityMode';
this.jsonAllConfig.client[key] = this.lwm2mConfigFormGroup.get('clientKey').value;
if (this.lwm2mConfigFormGroup.get('bootstrapServer').value[securityMode] === SECURITY_CONFIG_MODE.PSK) {
this.lwm2mConfigFormGroup.get('bootstrapServer').patchValue({
clientSecretKey: this.jsonAllConfig.client[key]
}, {emitEvent: false});
this.jsonAllConfig.bootstrap.bootstrapServer.clientSecretKey = this.jsonAllConfig.client[key];
}
if (this.lwm2mConfigFormGroup.get('lwm2mServer').value[securityMode] === SECURITY_CONFIG_MODE.PSK) {
this.lwm2mConfigFormGroup.get('lwm2mServer').patchValue({
clientSecretKey: this.jsonAllConfig.client[key]
}, {emitEvent: false});
this.jsonAllConfig.bootstrap.lwm2mServer.clientSecretKey = this.jsonAllConfig.client[key];
}
this.upDateJsonAllConfig();
}
private upDateJsonAllConfig = (): void => {
this.data.jsonAllConfig = JSON.parse(JSON.stringify(this.jsonAllConfig));
this.lwm2mConfigFormGroup.patchValue({
jsonAllConfig: JSON.parse(JSON.stringify(this.jsonAllConfig))
}, {emitEvent: false});
this.lwm2mConfigFormGroup.markAsDirty();
}
private initLwm2mConfigFormGroup = (): FormGroup => {
if (SECURITY_CONFIG_MODE[this.jsonAllConfig.client.securityConfigClientMode.toString()] === SECURITY_CONFIG_MODE.PSK) {
const endpoint = 'endpoint';
this.data.endPoint = this.jsonAllConfig.client[endpoint];
}
return this.fb.group({
securityConfigClientMode: [SECURITY_CONFIG_MODE[this.jsonAllConfig.client.securityConfigClientMode.toString()], []],
identityPSK: ['', []],
clientKey: ['', []],
clientCertificate: [false, []],
bootstrapServer: [this.jsonAllConfig.bootstrap[this.bootstrapServer], []],
lwm2mServer: [this.jsonAllConfig.bootstrap[this.lwm2mServer], []],
bootstrapFormGroup: this.getServerGroup(),
lwm2mServerFormGroup: this.getServerGroup(),
endPoint: [this.data.endPoint, []],
jsonAllConfig: [this.jsonAllConfig, []]
});
}
private getServerGroup = (): FormGroup => {
return this.fb.group({
securityMode: [this.fb.control(SECURITY_CONFIG_MODE.NO_SEC), []],
clientPublicKeyOrId: ['', []],
clientSecretKey: ['', []]
});
}
save(): void {
this.upDateValueToJson();
this.data.endPoint = this.lwm2mConfigFormGroup.get('endPoint').value.split('\'').join('');
this.data.jsonAllConfig = this.jsonAllConfig;
if (this.lwm2mConfigFormGroup.get('securityConfigClientMode').value === SECURITY_CONFIG_MODE.PSK) {
const identity = 'identity';
this.data.endPoint = this.data.jsonAllConfig.client[identity];
}
this.dialogRef.close(this.data);
}
cancel(): void {
this.dialogRef.close(undefined);
}
}

66
ui-ngx/src/app/modules/home/pages/device/lwm2m/security-config.models.ts → ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts

@ -17,9 +17,6 @@
export const JSON_ALL_CONFIG = 'jsonAllConfig'; export const JSON_ALL_CONFIG = 'jsonAllConfig';
export const END_POINT = 'endPoint'; export const END_POINT = 'endPoint';
export const DEFAULT_END_POINT = 'default_client_lwm2m_end_point_no_sec'; export const DEFAULT_END_POINT = 'default_client_lwm2m_end_point_no_sec';
export const BOOTSTRAP_SERVERS = 'servers';
export const BOOTSTRAP_SERVER = 'bootstrapServer';
export const LWM2M_SERVER = 'lwm2mServer';
export const LEN_MAX_PSK = 64; export const LEN_MAX_PSK = 64;
export const LEN_MAX_PRIVATE_KEY = 134; export const LEN_MAX_PRIVATE_KEY = 134;
export const LEN_MAX_PUBLIC_KEY_RPK = 182; export const LEN_MAX_PUBLIC_KEY_RPK = 182;
@ -28,28 +25,28 @@ export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/;
export interface DeviceCredentialsDialogLwm2mData { export interface DeviceCredentialsDialogLwm2mData {
jsonAllConfig?: SecurityConfigModels; jsonAllConfig?: Lwm2mSecurityConfigModels;
endPoint?: string; endPoint?: string;
} }
export enum SECURITY_CONFIG_MODE { export enum Lwm2mSecurityType {
PSK = 'PSK', PSK = 'PSK',
RPK = 'RPK', RPK = 'RPK',
X509 = 'X509', X509 = 'X509',
NO_SEC = 'NO_SEC' NO_SEC = 'NO_SEC'
} }
export const SECURITY_CONFIG_MODE_NAMES = new Map<SECURITY_CONFIG_MODE, string>( export const Lwm2mSecurityTypeTranslationMap = new Map<Lwm2mSecurityType, string>(
[ [
[SECURITY_CONFIG_MODE.PSK, 'Pre-Shared Key'], [Lwm2mSecurityType.PSK, 'Pre-Shared Key'],
[SECURITY_CONFIG_MODE.RPK, 'Raw Public Key'], [Lwm2mSecurityType.RPK, 'Raw Public Key'],
[SECURITY_CONFIG_MODE.X509, 'X.509 Certificate'], [Lwm2mSecurityType.X509, 'X.509 Certificate'],
[SECURITY_CONFIG_MODE.NO_SEC, 'No Security'], [Lwm2mSecurityType.NO_SEC, 'No Security'],
] ]
); );
export interface ClientSecurityConfig { export interface ClientSecurityConfig {
securityConfigClientMode: string; securityConfigClientMode: Lwm2mSecurityType;
endpoint: string; endpoint: string;
identity: string; identity: string;
key: string; key: string;
@ -57,7 +54,7 @@ export interface ClientSecurityConfig {
} }
export interface ServerSecurityConfig { export interface ServerSecurityConfig {
securityMode: string; securityMode: Lwm2mSecurityType;
clientPublicKeyOrId?: string; clientPublicKeyOrId?: string;
clientSecretKey?: string; clientSecretKey?: string;
} }
@ -67,20 +64,19 @@ interface BootstrapSecurityConfig {
lwm2mServer: ServerSecurityConfig; lwm2mServer: ServerSecurityConfig;
} }
export interface SecurityConfigModels { export interface Lwm2mSecurityConfigModels {
client: ClientSecurityConfig; client: ClientSecurityConfig;
bootstrap: BootstrapSecurityConfig; bootstrap: BootstrapSecurityConfig;
} }
export function getClientSecurityConfig(securityConfigMode: SECURITY_CONFIG_MODE, endPoint?: string): ClientSecurityConfig { export function getClientSecurityConfig(securityConfigMode: Lwm2mSecurityType, endPoint = ''): ClientSecurityConfig {
let security = getDefaultClientSecurityConfig(); const security = getDefaultClientSecurityConfig(securityConfigMode);
security.securityConfigClientMode = securityConfigMode.toString();
switch (securityConfigMode) { switch (securityConfigMode) {
case SECURITY_CONFIG_MODE.PSK: case Lwm2mSecurityType.PSK:
security.endpoint = endPoint; security.endpoint = endPoint;
security.identity = endPoint; security.identity = endPoint;
break; break;
case SECURITY_CONFIG_MODE.X509: case Lwm2mSecurityType.X509:
security.x509 = true; security.x509 = true;
break; break;
} }
@ -88,9 +84,9 @@ export function getClientSecurityConfig(securityConfigMode: SECURITY_CONFIG_MODE
return security; return security;
} }
export function getDefaultClientSecurityConfig(): ClientSecurityConfig { export function getDefaultClientSecurityConfig(securityConfigMode: Lwm2mSecurityType): ClientSecurityConfig {
return { return {
securityConfigClientMode: SECURITY_CONFIG_MODE.NO_SEC.toString(), securityConfigClientMode: securityConfigMode,
endpoint: '', endpoint: '',
identity: '', identity: '',
key: '', key: '',
@ -100,7 +96,7 @@ export function getDefaultClientSecurityConfig(): ClientSecurityConfig {
export function getDefaultServerSecurityConfig(): ServerSecurityConfig { export function getDefaultServerSecurityConfig(): ServerSecurityConfig {
return { return {
securityMode: SECURITY_CONFIG_MODE.NO_SEC.toString(), securityMode: Lwm2mSecurityType.NO_SEC,
clientPublicKeyOrId: '', clientPublicKeyOrId: '',
clientSecretKey: '' clientSecretKey: ''
}; };
@ -113,46 +109,42 @@ function getDefaultBootstrapSecurityConfig(): BootstrapSecurityConfig {
}; };
} }
export function getDefaultSecurityConfig(): SecurityConfigModels { export function getDefaultSecurityConfig(): Lwm2mSecurityConfigModels {
const securityConfigModels = { const securityConfigModels = {
client: getClientSecurityConfig(SECURITY_CONFIG_MODE.NO_SEC), client: getClientSecurityConfig(Lwm2mSecurityType.NO_SEC),
bootstrap: getDefaultBootstrapSecurityConfig() bootstrap: getDefaultBootstrapSecurityConfig()
}; };
return securityConfigModels; return securityConfigModels;
} }
const isSecurityConfigModels = (p: any): p is SecurityConfigModels => const isSecurityConfigModels = (p: any): boolean =>
p.hasOwnProperty('client') && p.hasOwnProperty('client') &&
isClientSecurityConfigType(p['client']) && isClientSecurityConfigType(p.client) &&
p.hasOwnProperty('bootstrap') && p.hasOwnProperty('bootstrap') &&
isBootstrapSecurityConfig(p['bootstrap']); isBootstrapSecurityConfig(p.bootstrap);
const isClientSecurityConfigType = (p: any): p is ClientSecurityConfig => const isClientSecurityConfigType = (p: any): boolean =>
p.hasOwnProperty('securityConfigClientMode') && p.hasOwnProperty('securityConfigClientMode') &&
p.hasOwnProperty('endpoint') && p.hasOwnProperty('endpoint') &&
p.hasOwnProperty('identity') && p.hasOwnProperty('identity') &&
p.hasOwnProperty('key') && p.hasOwnProperty('key') &&
p.hasOwnProperty('x509'); p.hasOwnProperty('x509');
const isBootstrapSecurityConfig = (p: any): p is BootstrapSecurityConfig => const isBootstrapSecurityConfig = (p: any): boolean =>
p.hasOwnProperty('bootstrapServer') && p.hasOwnProperty('bootstrapServer') &&
isServerSecurityConfig(p['bootstrapServer']) && isServerSecurityConfig(p.bootstrapServer) &&
p.hasOwnProperty('lwm2mServer') && p.hasOwnProperty('lwm2mServer') &&
isServerSecurityConfig(p['lwm2mServer']); isServerSecurityConfig(p.lwm2mServer);
const isServerSecurityConfig = (p: any): p is ServerSecurityConfig => const isServerSecurityConfig = (p: any): boolean =>
p.hasOwnProperty('securityMode') && p.hasOwnProperty('securityMode') &&
p.hasOwnProperty('clientPublicKeyOrId') && p.hasOwnProperty('clientPublicKeyOrId') &&
p.hasOwnProperty('clientSecretKey'); p.hasOwnProperty('clientSecretKey');
export function validateSecurityConfig(config: string): boolean { export function validateSecurityConfig(config: string): boolean {
try { try {
const securityConfig= JSON.parse(config); const securityConfig = JSON.parse(config);
if (isSecurityConfigModels(securityConfig)) { return isSecurityConfigModels(securityConfig);
return true;
} else {
return false;
}
} catch (e) { } catch (e) {
return false; return false;
} }

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

@ -928,9 +928,13 @@
"lwm2m-credentials-value-tip": "Edit security config json editor", "lwm2m-credentials-value-tip": "Edit security config json editor",
"lwm2m-security-config": { "lwm2m-security-config": {
"identity": "Client Identity", "identity": "Client Identity",
"identity-required": "Client Identity is required.",
"client-key": "Client Key", "client-key": "Client Key",
"required": " value is required.", "client-key-required": "Client Key is required.",
"client-key-pattern": "Client Key must be hexadecimal format.",
"client-key-length": "Client Key must be {{ count }} characters.",
"endpoint": "Endpoint Client Name", "endpoint": "Endpoint Client Name",
"endpoint-required": "Endpoint Client Name is required.",
"mode": "Security config mode", "mode": "Security config mode",
"client-tab": "Client Security Config", "client-tab": "Client Security Config",
"client-certificate": "Client certificate", "client-certificate": "Client certificate",
@ -938,9 +942,14 @@
"bootstrap-server": "Bootstrap Server", "bootstrap-server": "Bootstrap Server",
"lwm2m-server": "LwM2M Server", "lwm2m-server": "LwM2M Server",
"client-publicKey-or-id": "Client Public Key or Id", "client-publicKey-or-id": "Client Public Key or Id",
"client-publicKey-or-id-required": "Client Public Key or Id is required.",
"client-publicKey-or-id-pattern": "Client Public Key or Id must be hexadecimal format.",
"client-publicKey-or-id-length": "Client Public Key or Id must be {{ count }} characters.",
"client-secret-key": "Client Secret Key", "client-secret-key": "Client Secret Key",
"config-json-tab": "Json Client Security Config", "client-secret-key-required": "Client Secret Key is required.",
"pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }" "client-secret-key-pattern": "Client Secret Key must be hexadecimal format.",
"client-secret-key-length": "Client Secret Key must be {{ count }} characters.",
"config-json-tab": "Json Client Security Config"
}, },
"client-id": "Client ID", "client-id": "Client ID",
"client-id-pattern": "Contains invalid character.", "client-id-pattern": "Contains invalid character.",

Loading…
Cancel
Save