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-select formControlName="credentialsType">
<mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">
{{ credentialTypeNamesMap.get(deviceCredentialsType[credentialsType]) }}
{{ credentialTypeNamesMap.get(credentialsType) }}
</mat-option>
</mat-select>
</mat-form-field>
@ -85,7 +85,7 @@
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-value</mat-label>
<textarea matInput formControlName="credentialsValue" rows="10" required
[matTooltip]="lwm2mCredentialsValueTip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
[matTooltip]="lwm2mCredentialsValueTooltip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
matTooltipPosition="above"
></textarea>
<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.
///
import {Component, forwardRef, Input, OnDestroy, OnInit} from '@angular/core';
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
@ -33,9 +33,9 @@ import {
DeviceCredentials,
DeviceCredentialsType
} from '@shared/models/device.models';
import {Subscription} from 'rxjs';
import {distinctUntilChanged} from 'rxjs/operators';
import {SecurityConfigComponent} from '@home/pages/device/lwm2m/security-config.component';
import { Subject } from 'rxjs';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import {
ClientSecurityConfig,
DEFAULT_END_POINT,
@ -44,10 +44,10 @@ import {
getDefaultSecurityConfig,
JSON_ALL_CONFIG,
validateSecurityConfig
} from '@home/pages/device/lwm2m/security-config.models';
import {TranslateService} from '@ngx-translate/core';
import {MatDialog} from '@angular/material/dialog';
import {isDefinedAndNotNull} from '@core/utils';
} from '@shared/models/lwm2m-security-config.models';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { isDefinedAndNotNull } from '@core/utils';
@Component({
selector: 'tb-device-credentials',
@ -67,20 +67,16 @@ import {isDefinedAndNotNull} from '@core/utils';
})
export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy {
deviceCredentialsFormGroup: FormGroup;
subscriptions: Subscription[] = [];
@Input()
disabled: boolean;
deviceCredentials: DeviceCredentials = null;
private destroy$ = new Subject();
submitted = false;
deviceCredentialsFormGroup: FormGroup;
deviceCredentialsType = DeviceCredentialsType;
credentialsTypes = Object.keys(DeviceCredentialsType);
credentialsTypes = Object.values(DeviceCredentialsType);
credentialTypeNamesMap = credentialTypeNames;
@ -102,16 +98,17 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])})
});
this.deviceCredentialsFormGroup.get('credentialsBasic').disable();
this.subscriptions.push(
this.deviceCredentialsFormGroup.valueChanges.pipe(distinctUntilChanged()).subscribe(() => {
this.updateView();
})
);
this.subscriptions.push(
this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.subscribe(() => {
this.credentialsTypeChanged();
})
);
this.deviceCredentialsFormGroup.valueChanges.pipe(
distinctUntilChanged(),
takeUntil(this.destroy$)
).subscribe(() => {
this.updateView();
});
this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((type) => {
this.credentialsTypeChanged(type);
});
}
ngOnInit(): void {
@ -121,12 +118,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}
ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe());
this.destroy$.next();
this.destroy$.complete();
}
writeValue(value: DeviceCredentials | null): void {
if (isDefinedAndNotNull(value)) {
this.deviceCredentials = value;
let credentialsBasic = {clientId: null, userName: null, password: null};
let credentialsValue = null;
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({
credentialsId: null,
credentialsValue: JSON.stringify(getDefaultSecurityConfig(), null, 2),
credentialsValue,
credentialsBasic: {clientId: '', userName: '', password: ''}
});
this.updateValidators();
@ -264,7 +262,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}
}
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,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
@ -275,8 +273,8 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
(res) => {
if (res) {
this.deviceCredentialsFormGroup.patchValue({
credentialsValue: this.isDefautLw2mResponse(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('')
credentialsValue: this.isDefaultLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]),
credentialsId: this.isDefaultLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('')
});
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) === '[{}]';
}
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 {
let jsonConfigDef = JSON.stringify(getDefaultSecurityConfig(), null, 2);
return !flag ? "" : 'Example (mode=\"NoSec\"):\n\r ' + jsonConfigDef;
lwm2mCredentialsValueTooltip(flag: boolean): string {
return !flag ? '' : 'Example (mode=\"NoSec\"):\n\r ' + this.lwm2mDefaultConfig;
}
}

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 { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.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({
declarations:
@ -239,6 +241,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleDialogComponent,
EditAlarmDetailsDialogComponent,
SmsProviderConfigurationComponent,
@ -342,6 +346,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleInfoComponent,
AlarmScheduleComponent,
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.
///
import {Component, Inject, OnInit, SkipSelf} from "@angular/core";
import {ErrorStateMatcher} from "@angular/material/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, 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 { Component, Inject, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/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, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point';
export interface Lwm2mAttributesDialogData {
readonly: boolean;
attributeLwm2m: JsonObject;
destName: string
destName: string;
}
@Component({
@ -37,7 +36,8 @@ export interface Lwm2mAttributesDialogData {
styleUrls: ['./lwm2m-attributes.component.scss'],
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;
@ -54,8 +54,7 @@ export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttribu
@Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>,
private fb: FormBuilder,
public translate: TranslateService) {
private fb: FormBuilder) {
super(store, router, dialogRef);
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.
///
import {Component, forwardRef, Input, OnInit} from "@angular/core";
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
@ -26,17 +26,17 @@ import {
NG_VALUE_ACCESSOR,
Validator,
Validators
} from "@angular/forms";
import {Subscription} from "rxjs";
import {PageComponent} from "@shared/components/page.component";
import {Store} from "@ngrx/store";
import {AppState} from "@core/core.state";
} from '@angular/forms';
import { Subscription } from 'rxjs';
import {
ATTRIBUTE_KEYS,
ATTRIBUTE_LWM2M_ENUM,
ATTRIBUTE_LWM2M_MAP
} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models";
import {isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull} from "@core/utils";
} from './lwm2m-profile-config.models';
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({
@ -98,7 +98,7 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
registerOnTouched(fn: any): void {
}
setDisabledState?(isDisabled: boolean): void {
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
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;
kvList.controls.forEach(fg => {
if (fg.get('key').value==='ver') {
if (fg.get('key').value === 'ver') {
fg.get('value').setValidators(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())) {
keyValMap[entry.key] = entry.value.toString();
} else {
keyValMap[entry.key] = Number(entry.value)
keyValMap[entry.key] = Number(entry.value);
}
});
this.propagateChange(keyValMap);
@ -215,13 +215,13 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
private attributeLwm2mValueNumberValidator = (control: AbstractControl) => {
if (isNaN(Number(control.value)) || Number(control.value) < 0) {
return {
'validAttributeValue': true
validAttributeValue: true
};
}
return null;
}
private attributeLwm2mValueValidator = (property: string): Object [] => {
return property === 'ver'? [] : [this.attributeLwm2mValueNumberValidator];
private attributeLwm2mValueValidator = (property: string): object[] => {
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.
///
import {Component, EventEmitter, forwardRef, Inject, Input, Output} from "@angular/core";
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from "@angular/forms";
import {coerceBooleanProperty} from "@angular/cdk/coercion";
import {Store} from "@ngrx/store";
import {AppState} from "@core/core.state";
import {DeviceProfileService} from "@core/http/device-profile.service";
import {WINDOW} from "@core/services/window.service";
import {deepClone, isDefinedAndNotNull, isEmpty} from "@core/utils";
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";
import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { deepClone, isDefinedAndNotNull, isEmpty } from '@core/utils';
import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models';
@Component({
@ -46,7 +39,6 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL;
private requiredValue: boolean;
private dirty = false;
@Input()
attributeLwm2m: {};
@ -71,12 +63,9 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
private propagateChange = (v: any) => {
}
constructor(private store: Store<AppState>,
private dialog: MatDialog,
constructor(private dialog: MatDialog,
private fb: FormBuilder,
private deviceProfileService: DeviceProfileService,
private translate: TranslateService,
@Inject(WINDOW) private window: Window) {}
private translate: TranslateService) {}
registerOnChange(fn: any): void {
this.propagateChange = fn;
@ -117,20 +106,20 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
return label;
}
isDisableBtn (): boolean {
isDisableBtn(): boolean {
return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) &&
!isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled;
}
isIconView (): boolean {
isIconView(): boolean {
return this.isAttributeTelemetry || this.disabled;
}
isIconEditAdd (): boolean {
isIconEditAdd(): boolean {
return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m);
}
isToolTipLabel (): string {
isToolTipLabel(): string {
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.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip');
@ -140,7 +129,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
if ($event) {
$event.stopPropagation();
}
this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, Object>(Lwm2mAttributesDialogComponent, {
this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, object>(Lwm2mAttributesDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
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.
///
import {Component, forwardRef, Inject, Input} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import { Component, forwardRef, Inject, Input } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
DEFAULT_CLIENT_HOLD_OFF_TIME,
DEFAULT_ID_SERVER,
@ -28,13 +28,12 @@ import {
SECURITY_CONFIG_MODE_NAMES,
ServerSecurityConfig
} from './lwm2m-profile-config.models';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {WINDOW} from '@core/services/window.service';
import {pairwise, startWith} from 'rxjs/operators';
import {DeviceProfileService} from '@core/http/device-profile.service';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { WINDOW } from '@core/services/window.service';
import { pairwise, startWith } from 'rxjs/operators';
import { DeviceProfileService } from '@core/http/device-profile.service';
// @dynamic
@Component({
selector: 'tb-profile-lwm2m-device-config-server',
templateUrl: './lwm2m-device-config-server.component.html',
@ -73,8 +72,7 @@ export class Lwm2mDeviceConfigServerComponent implements ControlValueAccessor {
this.requiredValue = coerceBooleanProperty(value);
}
constructor(protected store: Store<AppState>,
public fb: FormBuilder,
constructor(public fb: FormBuilder,
private deviceProfileService: DeviceProfileService,
@Inject(WINDOW) private window: Window) {
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 { 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 { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
ATTRIBUTE,
@ -37,7 +35,6 @@ import {
} from './lwm2m-profile-config.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
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 { Direction } from '@shared/models/page/sort-order';
@ -78,10 +75,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private propagateChange = (v: any) => {
}
constructor(private store: Store<AppState>,
private fb: FormBuilder,
private deviceProfileService: DeviceProfileService,
@Inject(WINDOW) private window: Window) {
constructor(private fb: FormBuilder,
private deviceProfileService: DeviceProfileService) {
this.lwm2mDeviceProfileFormGroup = this.fb.group({
clientOnlyObserveAfterConnect: [1, []],
objectIds: [null, Validators.required],
@ -155,7 +150,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.lwm2mDeviceProfileFormGroup.patchValue({
clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect,
objectIds: value,
observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value['objectsList']),
observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList),
shortId: this.configurationValue.bootstrap.servers.shortId,
lifetime: this.configurationValue.bootstrap.servers.lifetime,
defaultMinPeriod: this.configurationValue.bootstrap.servers.defaultMinPeriod,
@ -171,7 +166,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
let configuration: DeviceProfileTransportConfiguration = null;
if (this.lwm2mDeviceConfigFormGroup.valid && this.lwm2mDeviceProfileFormGroup.valid) {
configuration = this.lwm2mDeviceConfigFormGroup.value.configurationJson;
// configuration.type = DeviceTransportType.LWM2M;
}
this.propagateChange(configuration);
}
@ -206,7 +200,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) {
const attributeArray = this.configurationValue.observeAttr.attribute;
const telemetryArray = this.configurationValue.observeAttr.telemetry;
let keyNameJson = this.configurationValue.observeAttr.keyName;
const keyNameJson = this.configurationValue.observeAttr.keyName;
if (this.includesNotZeroInstance(attributeArray, telemetryArray)) {
this.addInstances(attributeArray, telemetryArray, objectLwM2MS);
}
@ -270,9 +264,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private updateAttributeLwm2m = (objectLwM2MS: ObjectLwM2M[]): void => {
Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => {
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) {
let instance = objectLwM2M.instances.find(instance => instance.id === +instanceId)
const instance = objectLwM2M.instances.find(obj => obj.id === +instanceId);
if (instance && resourceId) {
instance.resources.find(resource => resource.id === +resourceId)
.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
@ -288,7 +282,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private updateKeyNameObjects = (objectLwM2MS: ObjectLwM2M[]): void => {
Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => {
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) {
objectLwM2M.instances.find(instance => instance.id === +instanceId)
.resources.find(resource => resource.id === +resourceId)
@ -299,7 +293,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => {
const keyName = JSON.parse(JSON.stringify(nameJson));
let keyNameValidate = {};
const keyNameValidate = {};
const keyAttrTelemetry = attributeArray.concat(telemetryArray);
Object.keys(keyName).forEach(key => {
if (keyAttrTelemetry.includes(key)) {
@ -345,9 +339,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
attributeLwm2m[pathRes] = resource.attributeLwm2m;
}
}
})
});
}
})
});
}
});
if (isUndefined(this.configurationValue.observeAttr)) {
@ -356,7 +350,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
attribute: attributeArray,
telemetry: telemetryArray,
keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew),
attributeLwm2m: attributeLwm2m
attributeLwm2m
};
} else {
this.configurationValue.observeAttr.observe = observeArray;
@ -457,7 +451,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
private removeAttributeLwm2mFromJson = (keyId: string): void => {
debugger
const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m;
Object.keys(keyNameJson).forEach(key => {
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.
///
import {Component, Inject, OnInit} from '@angular/core';
import {DialogComponent} from '@shared/components/dialog.component';
import {FormBuilder, FormGroup} from '@angular/forms';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {Router} from '@angular/router';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import { Component, Inject, OnInit } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
export interface Lwm2mObjectAddInstancesData {
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.
///
import {Component, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './lwm2m-profile-config.models';
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER } from './lwm2m-profile-config.models';
@Component({
selector: 'tb-profile-lwm2m-object-add-instances-list',
@ -41,8 +39,7 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
private fb: FormBuilder) {
constructor(private fb: FormBuilder) {
this.instancesListFormGroup = this.fb.group({
instanceIdInput: [null, [
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.
///
import {Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {Observable} from 'rxjs';
import {filter, map, mergeMap, publishReplay, refCount, tap} from 'rxjs/operators';
import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './lwm2m-profile-config.models';
import {DeviceProfileService} from '@core/http/device-profile.service';
import {Direction} from '@shared/models/page/sort-order';
import {isDefined, isDefinedAndNotNull, isString} from '@core/utils';
import {PageLink} from "@shared/models/page/page-link";
import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Observable } from 'rxjs';
import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { Direction } from '@shared/models/page/sort-order';
import { isDefined, isDefinedAndNotNull, isString } from '@core/utils';
import { PageLink } from '@shared/models/page/page-link';
@Component({
selector: 'tb-profile-lwm2m-object-list',
@ -71,8 +69,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
private propagateChange = (v: any) => {
}
constructor(private store: Store<AppState>,
private deviceProfileService: DeviceProfileService,
constructor(private deviceProfileService: DeviceProfileService,
private fb: FormBuilder) {
this.lwm2mListFormGroup = this.fb.group({
objectsList: [this.objectsList],
@ -162,20 +159,20 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => {
this.searchText = searchText;
return this.getLwM2mModelsPage().pipe(
map(objectLwM2Ms => objectLwM2Ms)
);
return this.getLwM2mModelsPage().pipe(
map(objectLwM2Ms => objectLwM2Ms)
);
}
private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> {
const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, {
property: 'id',
direction: Direction.ASC
});
this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe(
publishReplay(1),
refCount()
);
const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, {
property: 'id',
direction: Direction.ASC
});
this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe(
publishReplay(1),
refCount()
);
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.
///
import {Component, forwardRef, Input} from '@angular/core';
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 {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import { Component, forwardRef, Input } from '@angular/core';
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 _ from 'lodash';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
@Component({
selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource',
@ -54,8 +52,7 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
}
}
constructor(private store: Store<AppState>,
private fb: FormBuilder) {
constructor(private fb: FormBuilder) {
this.resourceFormGroup = this.fb.group({
resources: this.fb.array([])
});
@ -95,7 +92,7 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
}
getNameResourceLwm2m = (resourceLwM2M: ResourceLwM2M): string => {
return '<' + resourceLwM2M.id +'> ' + resourceLwM2M.name;
return `<${resourceLwM2M.id}> ${resourceLwM2M.name}`;
}
createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void {
@ -135,14 +132,14 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
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) {
this.resourceFormArray.at(index).patchValue({observe: false});
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;
}
}

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.
///
import {Component, forwardRef, Input} from '@angular/core';
import { Component, forwardRef, Input } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
@ -24,9 +24,7 @@ import {
NG_VALUE_ACCESSOR,
Validators
} from '@angular/forms';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
ATTRIBUTE,
ATTRIBUTE_LWM2M,
@ -38,9 +36,8 @@ import {
RESOURCES,
TELEMETRY
} from './lwm2m-profile-config.models';
import {deepClone, isDefinedAndNotNull, isEqual, isUndefined} from '@core/utils';
import {MatDialog} from '@angular/material/dialog';
import {TranslateService} from '@ngx-translate/core';
import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils';
import { MatDialog } from '@angular/material/dialog';
import {
Lwm2mObjectAddInstancesData,
Lwm2mObjectAddInstancesDialogComponent
@ -83,10 +80,8 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
@Input()
disabled: boolean;
constructor(private store: Store<AppState>,
private fb: FormBuilder,
private dialog: MatDialog,
public translate: TranslateService) {
constructor(private fb: FormBuilder,
private dialog: MatDialog) {
this.observeAttrTelemetryFormGroup = this.fb.group({
[CLIENT_LWM2M]: this.fb.array([])
});
@ -98,7 +93,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
}
private propagateChange = (v: any) => {
};
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
@ -189,7 +184,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).updateValueAndValidity();
}
trackByParams = (index: number, element: any): number => {
trackByParams = (index: number): number => {
return index;
}
@ -252,7 +247,6 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
}
private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => {
debugger
const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
.find(e => e.value.keyId === data.objectKeyId) as FormGroup;
const instancesArray = objectLwM2MFormGroup.value.instances as Instance [];
@ -318,7 +312,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
return objectName + ' <' + idVerObj + '>';
}
getNameInstanceLwm2m = (instance: Instance, idVerObj: string): string => {
return ' instance <' + idVerObj + '/' + instance.id +'>';
return ` instance <${idVerObj}/${instance.id}>`;
}
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.
///
import {NgModule} from '@angular/core';
import {Lwm2mDeviceProfileTransportConfigurationComponent} from './lwm2m-device-profile-transport-configuration.component';
import {Lwm2mObjectListComponent} from './lwm2m-object-list.component';
import {Lwm2mObserveAttrTelemetryComponent} from './lwm2m-observe-attr-telemetry.component';
import {Lwm2mObserveAttrTelemetryResourceComponent} from './lwm2m-observe-attr-telemetry-resource.component';
import {Lwm2mAttributesDialogComponent} from './lwm2m-attributes-dialog.component';
import {Lwm2mAttributesComponent} from './lwm2m-attributes.component';
import {Lwm2mAttributesKeyListComponent} from './lwm2m-attributes-key-list.component';
import {Lwm2mDeviceConfigServerComponent} from './lwm2m-device-config-server.component';
import {Lwm2mObjectAddInstancesDialogComponent} from './lwm2m-object-add-instances-dialog.component';
import {Lwm2mObjectAddInstancesListComponent} from './lwm2m-object-add-instances-list.component';
import {CommonModule} from '@angular/common';
import {SharedModule} from '@app/shared/shared.module';
import { NgModule } from '@angular/core';
import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component';
import { Lwm2mObjectListComponent } from './lwm2m-object-list.component';
import { Lwm2mObserveAttrTelemetryComponent } from './lwm2m-observe-attr-telemetry.component';
import { Lwm2mObserveAttrTelemetryResourceComponent } from './lwm2m-observe-attr-telemetry-resource.component';
import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component';
import { Lwm2mAttributesComponent } from './lwm2m-attributes.component';
import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component';
import { Lwm2mDeviceConfigServerComponent } from './lwm2m-device-config-server.component';
import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-instances-dialog.component';
import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@app/shared/shared.module';
@NgModule({
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 {
objectIds: string[],
objectsList: ObjectLwM2M[]
objectIds: string[];
objectsList: ObjectLwM2M[];
}
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 { HomeComponentsModule } from '@modules/home/components/home-components.module';
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 { DeviceConfigurationComponent } from './data/device-configuration.component';
import { DeviceDataComponent } from './data/device-data.component';
@ -50,9 +48,7 @@ import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-tr
DeviceComponent,
DeviceTabsComponent,
DeviceTableHeaderComponent,
DeviceCredentialsDialogComponent,
SecurityConfigComponent,
SecurityConfigServerComponent
DeviceCredentialsDialogComponent
],
imports: [
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 END_POINT = 'endPoint';
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_PRIVATE_KEY = 134;
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 {
jsonAllConfig?: SecurityConfigModels;
jsonAllConfig?: Lwm2mSecurityConfigModels;
endPoint?: string;
}
export enum SECURITY_CONFIG_MODE {
export enum Lwm2mSecurityType {
PSK = 'PSK',
RPK = 'RPK',
X509 = 'X509',
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'],
[SECURITY_CONFIG_MODE.RPK, 'Raw Public Key'],
[SECURITY_CONFIG_MODE.X509, 'X.509 Certificate'],
[SECURITY_CONFIG_MODE.NO_SEC, 'No Security'],
[Lwm2mSecurityType.PSK, 'Pre-Shared Key'],
[Lwm2mSecurityType.RPK, 'Raw Public Key'],
[Lwm2mSecurityType.X509, 'X.509 Certificate'],
[Lwm2mSecurityType.NO_SEC, 'No Security'],
]
);
export interface ClientSecurityConfig {
securityConfigClientMode: string;
securityConfigClientMode: Lwm2mSecurityType;
endpoint: string;
identity: string;
key: string;
@ -57,7 +54,7 @@ export interface ClientSecurityConfig {
}
export interface ServerSecurityConfig {
securityMode: string;
securityMode: Lwm2mSecurityType;
clientPublicKeyOrId?: string;
clientSecretKey?: string;
}
@ -67,20 +64,19 @@ interface BootstrapSecurityConfig {
lwm2mServer: ServerSecurityConfig;
}
export interface SecurityConfigModels {
export interface Lwm2mSecurityConfigModels {
client: ClientSecurityConfig;
bootstrap: BootstrapSecurityConfig;
}
export function getClientSecurityConfig(securityConfigMode: SECURITY_CONFIG_MODE, endPoint?: string): ClientSecurityConfig {
let security = getDefaultClientSecurityConfig();
security.securityConfigClientMode = securityConfigMode.toString();
export function getClientSecurityConfig(securityConfigMode: Lwm2mSecurityType, endPoint = ''): ClientSecurityConfig {
const security = getDefaultClientSecurityConfig(securityConfigMode);
switch (securityConfigMode) {
case SECURITY_CONFIG_MODE.PSK:
case Lwm2mSecurityType.PSK:
security.endpoint = endPoint;
security.identity = endPoint;
break;
case SECURITY_CONFIG_MODE.X509:
case Lwm2mSecurityType.X509:
security.x509 = true;
break;
}
@ -88,9 +84,9 @@ export function getClientSecurityConfig(securityConfigMode: SECURITY_CONFIG_MODE
return security;
}
export function getDefaultClientSecurityConfig(): ClientSecurityConfig {
export function getDefaultClientSecurityConfig(securityConfigMode: Lwm2mSecurityType): ClientSecurityConfig {
return {
securityConfigClientMode: SECURITY_CONFIG_MODE.NO_SEC.toString(),
securityConfigClientMode: securityConfigMode,
endpoint: '',
identity: '',
key: '',
@ -100,7 +96,7 @@ export function getDefaultClientSecurityConfig(): ClientSecurityConfig {
export function getDefaultServerSecurityConfig(): ServerSecurityConfig {
return {
securityMode: SECURITY_CONFIG_MODE.NO_SEC.toString(),
securityMode: Lwm2mSecurityType.NO_SEC,
clientPublicKeyOrId: '',
clientSecretKey: ''
};
@ -113,46 +109,42 @@ function getDefaultBootstrapSecurityConfig(): BootstrapSecurityConfig {
};
}
export function getDefaultSecurityConfig(): SecurityConfigModels {
export function getDefaultSecurityConfig(): Lwm2mSecurityConfigModels {
const securityConfigModels = {
client: getClientSecurityConfig(SECURITY_CONFIG_MODE.NO_SEC),
client: getClientSecurityConfig(Lwm2mSecurityType.NO_SEC),
bootstrap: getDefaultBootstrapSecurityConfig()
};
return securityConfigModels;
}
const isSecurityConfigModels = (p: any): p is SecurityConfigModels =>
const isSecurityConfigModels = (p: any): boolean =>
p.hasOwnProperty('client') &&
isClientSecurityConfigType(p['client']) &&
isClientSecurityConfigType(p.client) &&
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('endpoint') &&
p.hasOwnProperty('identity') &&
p.hasOwnProperty('key') &&
p.hasOwnProperty('x509');
const isBootstrapSecurityConfig = (p: any): p is BootstrapSecurityConfig =>
const isBootstrapSecurityConfig = (p: any): boolean =>
p.hasOwnProperty('bootstrapServer') &&
isServerSecurityConfig(p['bootstrapServer']) &&
isServerSecurityConfig(p.bootstrapServer) &&
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('clientPublicKeyOrId') &&
p.hasOwnProperty('clientSecretKey');
export function validateSecurityConfig(config: string): boolean {
try {
const securityConfig= JSON.parse(config);
if (isSecurityConfigModels(securityConfig)) {
return true;
} else {
return false;
}
const securityConfig = JSON.parse(config);
return isSecurityConfigModels(securityConfig);
} catch (e) {
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-security-config": {
"identity": "Client Identity",
"identity-required": "Client Identity is required.",
"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-required": "Endpoint Client Name is required.",
"mode": "Security config mode",
"client-tab": "Client Security Config",
"client-certificate": "Client certificate",
@ -938,9 +942,14 @@
"bootstrap-server": "Bootstrap Server",
"lwm2m-server": "LwM2M Server",
"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",
"config-json-tab": "Json Client Security Config",
"pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }"
"client-secret-key-required": "Client Secret Key is required.",
"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-pattern": "Contains invalid character.",

Loading…
Cancel
Save