Browse Source

UI: Refactoring lwm2m security config and add X509 certificate in lwm2m security model

pull/4624/head
Vladyslav_Prykhodko 5 years ago
parent
commit
53c51953c4
  1. 29
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.html
  2. 90
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts
  3. 5
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts
  4. 232
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html
  5. 262
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts
  6. 99
      ui-ngx/src/app/shared/models/lwm2m-security-config.models.ts
  7. 12
      ui-ngx/src/assets/locale/locale.constant-en_US.json

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

@ -75,32 +75,7 @@
('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>
</section>
<div *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.LWM2M_CREDENTIALS">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-key</mat-label>
<input matInput type="text" formControlName="credentialsId" required>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')">
{{ 'device.lwm2m-key-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-value</mat-label>
<textarea matInput formControlName="credentialsValue" rows="10" required
[matTooltip]="lwm2mCredentialsValueTooltip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
matTooltipPosition="above"
></textarea>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">
{{ 'device.lwm2m-value-required' | translate }}
</mat-error>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError')">
{{ 'device.lwm2m-value-format-error' | translate }}
</mat-error>
<div mat-dialog-actions fxLayoutAlign="center center">
<button mat-raised-button color="primary"
(click)="openSecurityInfoLwM2mDialog($event)"
>
{{'device.lwm2m-value-edit' | translate }}
</button>
</div>
</mat-form-field>
<tb-security-config-lwm2m formControlName="credentialsValue">
</tb-security-config-lwm2m>
</div>
</section>

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

@ -34,19 +34,7 @@ import {
DeviceCredentialsType
} from '@shared/models/device.models';
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,
DeviceCredentialsDialogLwm2mData,
END_POINT,
getDefaultSecurityConfig,
JSON_ALL_CONFIG,
validateSecurityConfig
} from '@shared/models/lwm2m-security-config.models';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { takeUntil } from 'rxjs/operators';
import { isDefinedAndNotNull } from '@core/utils';
@Component({
@ -84,9 +72,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
private propagateChange = (v: any) => {};
constructor(public fb: FormBuilder,
private translate: TranslateService,
private dialog: MatDialog) {
constructor(public fb: FormBuilder) {
this.deviceCredentialsFormGroup = this.fb.group({
credentialsType: [DeviceCredentialsType.ACCESS_TOKEN],
credentialsId: [null],
@ -99,15 +85,14 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
});
this.deviceCredentialsFormGroup.get('credentialsBasic').disable();
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);
).subscribe(() => {
this.credentialsTypeChanged();
});
}
@ -128,8 +113,6 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
let credentialsValue = null;
if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
credentialsBasic = JSON.parse(value.credentialsValue) as DeviceCredentialMQTTBasic;
} else if (value.credentialsType === DeviceCredentialsType.LWM2M_CREDENTIALS) {
credentialsValue = JSON.parse(JSON.stringify(value.credentialsValue)) as ClientSecurityConfig;
} else {
credentialsValue = value.credentialsValue;
}
@ -176,11 +159,10 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
};
}
credentialsTypeChanged(credentialsType: DeviceCredentialsType): void {
const credentialsValue = credentialsType === DeviceCredentialsType.LWM2M_CREDENTIALS ? this.lwm2mDefaultConfig : null;
credentialsTypeChanged(): void {
this.deviceCredentialsFormGroup.patchValue({
credentialsId: null,
credentialsValue,
credentialsValue: null,
credentialsBasic: {clientId: '', userName: '', password: ''}
});
this.updateValidators();
@ -198,14 +180,8 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});
break;
case DeviceCredentialsType.X509_CERTIFICATE:
this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]);
this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);
this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});
break;
case DeviceCredentialsType.LWM2M_CREDENTIALS:
this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required, this.lwm2mConfigJsonValidator]);
this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]);
this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);
this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
@ -245,56 +221,4 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
onlySelf: true
});
}
openSecurityInfoLwM2mDialog($event: Event): void {
if ($event) {
$event.stopPropagation();
$event.preventDefault();
}
let credentialsValue = this.deviceCredentialsFormGroup.get('credentialsValue').value;
if (credentialsValue === null || credentialsValue.length === 0) {
credentialsValue = getDefaultSecurityConfig();
} else {
try {
credentialsValue = JSON.parse(credentialsValue);
} catch (e) {
credentialsValue = getDefaultSecurityConfig();
}
}
const credentialsId = this.deviceCredentialsFormGroup.get('credentialsId').value || DEFAULT_END_POINT;
this.dialog.open<SecurityConfigLwm2mComponent, DeviceCredentialsDialogLwm2mData, object>(SecurityConfigLwm2mComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
jsonAllConfig: credentialsValue,
endPoint: credentialsId
}
}).afterClosed().subscribe(
(res) => {
if (res) {
this.deviceCredentialsFormGroup.patchValue({
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();
}
}
);
}
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'}};
}
private get lwm2mDefaultConfig(): string {
return JSON.stringify(getDefaultSecurityConfig(), null, 2);
}
lwm2mCredentialsValueTooltip(flag: boolean): string {
return !flag ? '' : 'Example (mode=\"NoSec\"):\n\r ' + this.lwm2mDefaultConfig;
}
}

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

@ -130,6 +130,8 @@ export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlVal
case Lwm2mSecurityType.NO_SEC:
this.serverFormGroup.get('clientPublicKeyOrId').clearValidators();
this.serverFormGroup.get('clientSecretKey').clearValidators();
this.serverFormGroup.get('clientPublicKeyOrId').disable({emitEvent: false});
this.serverFormGroup.get('clientSecretKey').disable();
break;
case Lwm2mSecurityType.PSK:
this.lenMinClientPublicKeyOrId = 0;
@ -172,5 +174,8 @@ export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlVal
Validators.minLength(this.lengthClientSecretKey),
Validators.maxLength(this.lengthClientSecretKey)
]);
this.serverFormGroup.get('clientPublicKeyOrId').enable({emitEvent: false});
this.serverFormGroup.get('clientSecretKey').enable();
}
}

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

@ -15,129 +15,109 @@
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>
<mat-tab-group>
<mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup">
<ng-container formGroupName="client">
<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('client.endpoint').hasError('required')">
{{ 'device.lwm2m-security-config.endpoint-required' | translate }}
</mat-error>
</mat-form-field>
<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>
<mat-form-field class="mat-block" *ngIf="lwm2mConfigFormGroup.get('client.securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-label>{{ 'device.lwm2m-security-config.identity' | translate }}</mat-label>
<input matInput type="text" formControlName="identity" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('client.identity').hasError('required')">
{{ 'device.lwm2m-security-config.identity-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" *ngIf="lwm2mConfigFormGroup.get('client.securityConfigClientMode').value === securityConfigLwM2MType.RPK ||
lwm2mConfigFormGroup.get('client.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('client.key').hasError('required')">
{{ 'device.lwm2m-security-config.client-key-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mConfigFormGroup.get('client.key').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-key-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(lwm2mConfigFormGroup.get('client.key').hasError('maxlength') ||
lwm2mConfigFormGroup.get('client.key').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-key-length' | translate: {
count: lenMaxKeyClient
} }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" *ngIf="lwm2mConfigFormGroup.get('client.securityConfigClientMode').value === securityConfigLwM2MType.X509">
<mat-label translate>device.lwm2m-security-config.client-public-key</mat-label>
<textarea matInput
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="cert">
</textarea>
<mat-hint translate>device.lwm2m-security-config.client-public-key-hint</mat-hint>
</mat-form-field>
</ng-container>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup">
<div style="padding: 2px;" formGroupName="bootstrap">
<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
[ngModel]="lwm2mConfigFormGroup.value"
label="{{ 'device.lwm2m-value' | translate }}"
readonly
[required]="true"
[fillHeight]="false">
</tb-json-object-edit>
</ng-template>
</mat-tab>
</mat-tab-group>

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

@ -14,19 +14,20 @@
/// 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 { Component, forwardRef, OnDestroy } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
Validators
} from '@angular/forms';
import {
DeviceCredentialsDialogLwm2mData,
getClientSecurityConfig,
JSON_ALL_CONFIG,
getDefaultClientSecurityConfig,
getDefaultServerSecurityConfig,
KEY_REGEXP_HEX_DEC,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
@ -34,48 +35,67 @@ import {
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';
import { isDefinedAndNotNull } from '@core/utils';
@Component({
selector: 'tb-security-config-lwm2m',
templateUrl: './security-config-lwm2m.component.html',
styleUrls: ['./security-config-lwm2m.component.scss']
styleUrls: ['./security-config-lwm2m.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SecurityConfigLwm2mComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => SecurityConfigLwm2mComponent),
multi: true
}
]
})
export class SecurityConfigLwm2mComponent extends DialogComponent<SecurityConfigLwm2mComponent, object> implements OnInit, OnDestroy {
private destroy$ = new Subject();
export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Validator, OnDestroy {
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);
private destroy$ = new Subject();
private propagateChange = (v: any) => {};
constructor(private fb: FormBuilder) {
this.lwm2mConfigFormGroup = this.initLwm2mConfigForm();
}
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'));
writeValue(obj: string) {
if (isDefinedAndNotNull(obj)) {
this.initClientSecurityConfig(JSON.parse(obj));
}
}
registerOnChange(fn: any) {
this.propagateChange = fn;
}
registerOnTouched(fn: any) {}
setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.lwm2mConfigFormGroup.disable({emitEvent: false});
} else {
this.lwm2mConfigFormGroup.enable({emitEvent: false});
}
}
validate(): ValidationErrors | null {
return this.lwm2mConfigFormGroup.valid ? null : {
securityConfigLWm2m: false
};
}
ngOnDestroy() {
@ -83,175 +103,97 @@ export class SecurityConfigLwm2mComponent extends DialogComponent<SecurityConfig
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 initClientSecurityConfig(config: Lwm2mSecurityConfigModels): void {
this.lwm2mConfigFormGroup.patchValue(config, {emitEvent: false});
this.securityConfigClientUpdateValidators(config.client.securityConfigClientMode);
}
private securityConfigClientModeChanged(type: Lwm2mSecurityType): void {
const config = getClientSecurityConfig(type, this.lwm2mConfigFormGroup.get('endPoint').value);
const config = getDefaultClientSecurityConfig(type, this.lwm2mConfigFormGroup.get('client.endpoint').value);
switch (type) {
case Lwm2mSecurityType.PSK:
config.identity = this.data.endPoint;
config.key = this.lwm2mConfigFormGroup.get('key').value;
config.key = this.lwm2mConfigFormGroup.get('client.key').value;
break;
case Lwm2mSecurityType.RPK:
config.key = this.lwm2mConfigFormGroup.get('key').value;
config.key = this.lwm2mConfigFormGroup.get('client.key').value;
break;
}
this.jsonAllConfig.client = config;
this.lwm2mConfigFormGroup.patchValue({
...config,
jsonAllConfig: this.jsonAllConfig
}, {emitEvent: false});
this.lwm2mConfigFormGroup.get('client').patchValue(config, {emitEvent: false});
this.securityConfigClientUpdateValidators(type);
}
private securityConfigClientUpdateValidators = (mode: Lwm2mSecurityType): void => {
switch (mode) {
case Lwm2mSecurityType.NO_SEC:
this.setValidatorsNoSecX509();
this.lwm2mConfigFormGroup.get('client.cert').disable();
break;
case Lwm2mSecurityType.X509:
this.setValidatorsNoSecX509();
this.lwm2mConfigFormGroup.get('client.cert').enable();
break;
case Lwm2mSecurityType.PSK:
this.lenMaxKeyClient = LEN_MAX_PSK;
this.setValidatorsPskRpk(mode);
this.lwm2mConfigFormGroup.get('client.identity').enable();
break;
case Lwm2mSecurityType.RPK:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
this.setValidatorsPskRpk(mode);
this.lwm2mConfigFormGroup.get('client.identity').disable();
break;
}
this.lwm2mConfigFormGroup.get('identity').updateValueAndValidity({emitEvent: false});
this.lwm2mConfigFormGroup.get('key').updateValueAndValidity({emitEvent: false});
this.lwm2mConfigFormGroup.get('client.identity').updateValueAndValidity({emitEvent: false});
this.lwm2mConfigFormGroup.get('client.key').updateValueAndValidity({emitEvent: false});
}
private setValidatorsNoSecX509 = (): void => {
this.lwm2mConfigFormGroup.get('identity').setValidators([]);
this.lwm2mConfigFormGroup.get('key').setValidators([]);
this.lwm2mConfigFormGroup.get('client.identity').clearValidators();
this.lwm2mConfigFormGroup.get('client.key').clearValidators();
this.lwm2mConfigFormGroup.get('client.identity').disable({emitEvent: false});
this.lwm2mConfigFormGroup.get('client.key').disable({emitEvent: false});
}
private setValidatorsPskRpk = (mode: Lwm2mSecurityType): void => {
if (mode === Lwm2mSecurityType.PSK) {
this.lwm2mConfigFormGroup.get('identity').setValidators([Validators.required]);
this.lwm2mConfigFormGroup.get('client.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.lwm2mConfigFormGroup.get('client.identity').clearValidators();
}
this.tabIndexPrevious = tabChangeEvent.index;
this.lwm2mConfigFormGroup.get('client.key').setValidators([
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.maxLength(this.lenMaxKeyClient),
Validators.minLength(this.lenMaxKeyClient)
]);
this.lwm2mConfigFormGroup.get('client.key').enable({emitEvent: false});
this.lwm2mConfigFormGroup.get('client.cert').disable({emitEvent: false});
}
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;
}
private initLwm2mConfigForm = (): FormGroup => {
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]
client: this.fb.group({
endpoint: ['', Validators.required],
securityConfigClientMode: [Lwm2mSecurityType.NO_SEC],
identity: [{value: '', disabled: true}],
key: [{value: '', disabled: true}],
cert: [{value: '', disabled: true}]
}),
bootstrap: this.fb.group({
bootstrapServer: [getDefaultServerSecurityConfig()],
lwm2mServer: [getDefaultServerSecurityConfig()]
})
});
formGroup.get('securityConfigClientMode').valueChanges.pipe(
formGroup.get('client.securityConfigClientMode').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((type) => {
this.securityConfigClientModeChanged(type);
});
formGroup.get('endPoint').valueChanges.pipe(
formGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((endpoint) => {
if (formGroup.get('securityConfigClientMode').value === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.client.endpoint = endpoint;
this.upDateJsonAllConfig();
}
).subscribe((value) => {
this.propagateChange(JSON.stringify(value));
});
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);
}
}

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

@ -14,21 +14,12 @@
/// limitations under the License.
///
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 LEN_MAX_PSK = 64;
export const LEN_MAX_PRIVATE_KEY = 134;
export const LEN_MAX_PUBLIC_KEY_RPK = 182;
export const LEN_MAX_PUBLIC_KEY_X509 = 3000;
export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/;
export interface DeviceCredentialsDialogLwm2mData {
jsonAllConfig?: Lwm2mSecurityConfigModels;
endPoint?: string;
}
export enum Lwm2mSecurityType {
PSK = 'PSK',
RPK = 'RPK',
@ -48,9 +39,9 @@ export const Lwm2mSecurityTypeTranslationMap = new Map<Lwm2mSecurityType, string
export interface ClientSecurityConfig {
securityConfigClientMode: Lwm2mSecurityType;
endpoint: string;
identity: string;
key: string;
x509: boolean;
identity?: string;
key?: string;
cert?: string;
}
export interface ServerSecurityConfig {
@ -69,85 +60,29 @@ export interface Lwm2mSecurityConfigModels {
bootstrap: BootstrapSecurityConfig;
}
export function getClientSecurityConfig(securityConfigMode: Lwm2mSecurityType, endPoint = ''): ClientSecurityConfig {
const security = getDefaultClientSecurityConfig(securityConfigMode);
export function getDefaultClientSecurityConfig(securityConfigMode: Lwm2mSecurityType, endPoint = ''): ClientSecurityConfig {
let security = {
securityConfigClientMode: securityConfigMode,
endpoint: endPoint,
identity: '',
key: '',
};
switch (securityConfigMode) {
case Lwm2mSecurityType.X509:
security = { ...security, ...{cert: ''}};
break;
case Lwm2mSecurityType.PSK:
security.endpoint = endPoint;
security.identity = endPoint;
security = { ...security, ...{identity: endPoint, key: ''}};
break;
case Lwm2mSecurityType.X509:
security.x509 = true;
case Lwm2mSecurityType.RPK:
security = { ...security, ...{key: ''}};
break;
}
return security;
}
export function getDefaultClientSecurityConfig(securityConfigMode: Lwm2mSecurityType): ClientSecurityConfig {
return {
securityConfigClientMode: securityConfigMode,
endpoint: '',
identity: '',
key: '',
x509: false
};
}
export function getDefaultServerSecurityConfig(): ServerSecurityConfig {
return {
securityMode: Lwm2mSecurityType.NO_SEC,
clientPublicKeyOrId: '',
clientSecretKey: ''
};
}
function getDefaultBootstrapSecurityConfig(): BootstrapSecurityConfig {
return {
bootstrapServer: getDefaultServerSecurityConfig(),
lwm2mServer: getDefaultServerSecurityConfig()
securityMode: Lwm2mSecurityType.NO_SEC
};
}
export function getDefaultSecurityConfig(): Lwm2mSecurityConfigModels {
const securityConfigModels = {
client: getClientSecurityConfig(Lwm2mSecurityType.NO_SEC),
bootstrap: getDefaultBootstrapSecurityConfig()
};
return securityConfigModels;
}
const isSecurityConfigModels = (p: any): boolean =>
p.hasOwnProperty('client') &&
isClientSecurityConfigType(p.client) &&
p.hasOwnProperty('bootstrap') &&
isBootstrapSecurityConfig(p.bootstrap);
const isClientSecurityConfigType = (p: any): boolean =>
p.hasOwnProperty('securityConfigClientMode') &&
p.hasOwnProperty('endpoint') &&
p.hasOwnProperty('identity') &&
p.hasOwnProperty('key') &&
p.hasOwnProperty('x509');
const isBootstrapSecurityConfig = (p: any): boolean =>
p.hasOwnProperty('bootstrapServer') &&
isServerSecurityConfig(p.bootstrapServer) &&
p.hasOwnProperty('lwm2mServer') &&
isServerSecurityConfig(p.lwm2mServer);
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);
return isSecurityConfigModels(securityConfig);
} catch (e) {
return false;
}
}

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

@ -917,15 +917,7 @@
"access-token-invalid": "Access token length must be from 1 to 20 characters.",
"rsa-key": "RSA public key",
"rsa-key-required": "RSA public key is required.",
"lwm2m-key": "LwM2M Security config key",
"lwm2m-key-required": "LwM2M Security config key is required.",
"lwm2m-value": "LwM2M Security config",
"lwm2m-value-required": "LwM2M Security config value is required.",
"lwm2m-value-format-error": "Security config value must be in LwM2M Security config format.",
"lwm2m-endpoint": "Client endpoint/identity",
"lwm2m-security-info": "Security Config Info",
"lwm2m-value-edit": "Edit Security config",
"lwm2m-credentials-value-tip": "Edit security config json editor",
"lwm2m-security-config": {
"identity": "Client Identity",
"identity-required": "Client Identity is required.",
@ -949,7 +941,9 @@
"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"
"config-json-tab": "Json Client Security Config",
"client-public-key": "Client public key",
"client-public-key-hint": "If client public key is empty, the trusted certificate will be used"
},
"client-id": "Client ID",
"client-id-pattern": "Contains invalid character.",

Loading…
Cancel
Save