diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html b/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html new file mode 100644 index 0000000000..0b798e6dbf --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html @@ -0,0 +1,48 @@ + +
+ + device.client-id + + + {{ 'device.client-id-pattern' | translate }} + + + + device.user-name + + + {{ 'device.user-name-required' | translate }} + + + + device.password + + + + +
diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.ts new file mode 100644 index 0000000000..d4c88431a9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.ts @@ -0,0 +1,133 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + ValidatorFn, + Validators +} from '@angular/forms'; +import { Subject } from 'rxjs'; +import { DeviceCredentialMQTTBasic } from '@shared/models/device.models'; +import { takeUntil } from 'rxjs/operators'; +import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; + +@Component({ + selector: 'tb-device-credentials-mqtt-basic', + templateUrl: './device-credentials-mqtt-basic.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent), + multi: true, + }], + styleUrls: [] +}) +export class DeviceCredentialsMqttBasicComponent implements ControlValueAccessor, Validator, OnDestroy { + + @Input() + disabled: boolean; + + deviceCredentialsMqttFormGroup: FormGroup; + + hidePassword = true; + + private destroy$ = new Subject(); + private propagateChange = (v: any) => {}; + + constructor(public fb: FormBuilder) { + this.deviceCredentialsMqttFormGroup = this.fb.group({ + clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]], + userName: [null], + password: [null] + }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])}); + this.deviceCredentialsMqttFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + this.updateView(value); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void {} + + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + if (this.disabled) { + this.deviceCredentialsMqttFormGroup.disable({emitEvent: false}); + } else { + this.deviceCredentialsMqttFormGroup.enable({emitEvent: false}); + } + } + + validate(): ValidationErrors | null { + return this.deviceCredentialsMqttFormGroup.valid ? null : { + deviceCredentialsMqttBasic: false + }; + } + + writeValue(mqttBasic: string) { + if (isDefinedAndNotNull(mqttBasic) && !isEmptyStr(mqttBasic)) { + const value = JSON.parse(mqttBasic); + this.deviceCredentialsMqttFormGroup.patchValue(value, {emitEvent: false}); + } + } + + updateView(value: DeviceCredentialMQTTBasic) { + const formValue = JSON.stringify(value); + this.propagateChange(formValue); + } + + passwordChanged() { + const value = this.deviceCredentialsMqttFormGroup.get('password').value; + if (value !== '') { + this.deviceCredentialsMqttFormGroup.get('userName').setValidators([Validators.required]); + } else { + this.deviceCredentialsMqttFormGroup.get('userName').setValidators([]); + } + this.deviceCredentialsMqttFormGroup.get('userName').updateValueAndValidity({emitEvent: false}); + } + + private atLeastOne(validator: ValidatorFn, controls: string[] = null) { + return (group: FormGroup): ValidationErrors | null => { + if (!controls) { + controls = Object.keys(group.controls); + } + const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k])); + + return hasAtLeastOne ? null : {atLeastOne: true}; + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html index 76c1fa13bb..76a9935565 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html @@ -24,58 +24,35 @@ - - device.access-token - - - {{ 'device.access-token-required' | translate }} - - - {{ 'device.access-token-invalid' | translate }} - - - - device.rsa-key - - - {{ 'device.rsa-key-required' | translate }} - - -
- - device.client-id - - - {{ 'device.client-id-pattern' | translate }} - - - - device.user-name - - - {{ 'device.user-name-required' | translate }} - - - - device.password - - - - -
-
- - +
+ + + device.access-token + + + {{ 'device.access-token-required' | translate }} + + + {{ 'device.access-token-invalid' | translate }} + + + + + + device.rsa-key + + + {{ 'device.rsa-key-required' | translate }} + + + + + + + + + + +
diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts index d558213ec4..3617ecbcf4 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts @@ -22,15 +22,12 @@ import { FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, - ValidationErrors, Validator, - ValidatorFn, Validators } from '@angular/forms'; import { credentialTypeNames, credentialTypesByTransportType, - DeviceCredentialMQTTBasic, DeviceCredentials, DeviceCredentialsType, DeviceTransportType @@ -62,7 +59,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, private deviceTransportTypeValue = DeviceTransportType.DEFAULT; get deviceTransportType(): DeviceTransportType { - return this.deviceTransportTypeValue + return this.deviceTransportTypeValue; } @Input() set deviceTransportType(type: DeviceTransportType) { @@ -86,22 +83,14 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, credentialTypeNamesMap = credentialTypeNames; - hidePassword = true; - private propagateChange = (v: any) => {}; constructor(public fb: FormBuilder) { this.deviceCredentialsFormGroup = this.fb.group({ credentialsType: [DeviceCredentialsType.ACCESS_TOKEN], credentialsId: [null], - credentialsValue: [null], - credentialsBasic: this.fb.group({ - clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]], - userName: [null], - password: [null] - }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])}) + credentialsValue: [null] }); - this.deviceCredentialsFormGroup.get('credentialsBasic').disable(); this.deviceCredentialsFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => { @@ -127,18 +116,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, writeValue(value: DeviceCredentials | null): void { if (isDefinedAndNotNull(value)) { - let credentialsBasic = {clientId: null, userName: null, password: null}; - let credentialsValue = null; - if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) { - credentialsBasic = JSON.parse(value.credentialsValue) as DeviceCredentialMQTTBasic; - } else { - credentialsValue = value.credentialsValue; - } + const credentialsType = this.credentialsTypes.includes(value.credentialsType) ? value.credentialsType : this.credentialsTypes[0]; this.deviceCredentialsFormGroup.patchValue({ - credentialsType: value.credentialsType, + credentialsType, credentialsId: value.credentialsId, - credentialsValue, - credentialsBasic + credentialsValue: value.credentialsValue }, {emitEvent: false}); this.updateValidators(); } @@ -146,10 +128,6 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, updateView() { const deviceCredentialsValue = this.deviceCredentialsFormGroup.value; - if (deviceCredentialsValue.credentialsType === DeviceCredentialsType.MQTT_BASIC) { - deviceCredentialsValue.credentialsValue = JSON.stringify(deviceCredentialsValue.credentialsBasic); - } - delete deviceCredentialsValue.credentialsBasic; this.propagateChange(deviceCredentialsValue); } @@ -181,14 +159,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, credentialsTypeChanged(): void { this.deviceCredentialsFormGroup.patchValue({ credentialsId: null, - credentialsValue: null, - credentialsBasic: {clientId: '', userName: '', password: ''} + credentialsValue: null }); this.updateValidators(); } updateValidators(): void { - this.hidePassword = true; const credentialsType = this.deviceCredentialsFormGroup.get('credentialsType').value as DeviceCredentialsType; switch (credentialsType) { case DeviceCredentialsType.ACCESS_TOKEN: @@ -196,48 +172,13 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false}); this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]); this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); - this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false}); break; - case DeviceCredentialsType.X509_CERTIFICATE: - case DeviceCredentialsType.LWM2M_CREDENTIALS: + default: 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.MQTT_BASIC: - this.deviceCredentialsFormGroup.get('credentialsBasic').enable({emitEvent: false}); - this.deviceCredentialsFormGroup.get('credentialsBasic').updateValueAndValidity({emitEvent: false}); - this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]); - this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false}); - this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]); - this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); - break; - } - } - - private atLeastOne(validator: ValidatorFn, controls: string[] = null) { - return (group: FormGroup): ValidationErrors | null => { - if (!controls) { - controls = Object.keys(group.controls); - } - const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k])); - - return hasAtLeastOne ? null : {atLeastOne: true}; - }; - } - - passwordChanged() { - const value = this.deviceCredentialsFormGroup.get('credentialsBasic.password').value; - if (value !== '') { - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([Validators.required]); - } else { - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([]); } - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').updateValueAndValidity({ - emitEvent: false, - onlySelf: true - }); } } diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.module.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials.module.ts new file mode 100644 index 0000000000..c3427bba68 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.module.ts @@ -0,0 +1,46 @@ +/// +/// 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 { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { CopyDeviceCredentialsComponent } from './copy-device-credentials.component'; +import { DeviceCredentialsComponent } from './device-credentials.component'; +import { SecurityConfigLwm2mComponent } from './security-config-lwm2m.component'; +import { SecurityConfigLwm2mServerComponent } from './security-config-lwm2m-server.component'; +import { DeviceCredentialsMqttBasicComponent } from './device-credentials-mqtt-basic.component'; + +@NgModule({ + declarations: [ + CopyDeviceCredentialsComponent, + DeviceCredentialsComponent, + SecurityConfigLwm2mComponent, + SecurityConfigLwm2mServerComponent, + DeviceCredentialsMqttBasicComponent + ], + imports: [ + CommonModule, + SharedModule + ], + exports: [ + CopyDeviceCredentialsComponent, + DeviceCredentialsComponent, + SecurityConfigLwm2mComponent, + SecurityConfigLwm2mServerComponent, + DeviceCredentialsMqttBasicComponent + ] +}) +export class DeviceCredentialsModule { } diff --git a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html index 77b7601ed5..200cd88f74 100644 --- a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html +++ b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html @@ -15,8 +15,8 @@ limitations under the License. --> - - + + device.lwm2m-security-config.endpoint @@ -79,7 +79,7 @@ - +
@@ -109,15 +109,4 @@
- - - - - -
diff --git a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts index eb920d6190..cbb85e305f 100644 --- a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts @@ -89,6 +89,7 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid this.lwm2mConfigFormGroup.disable({emitEvent: false}); } else { this.lwm2mConfigFormGroup.enable({emitEvent: false}); + this.securityConfigClientUpdateValidators(this.lwm2mConfigFormGroup.get('client.securityConfigClientMode').value); } } @@ -126,21 +127,21 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid switch (mode) { case Lwm2mSecurityType.NO_SEC: this.setValidatorsNoSecX509(); - this.lwm2mConfigFormGroup.get('client.cert').disable(); + this.lwm2mConfigFormGroup.get('client.cert').disable({emitEvent: false}); break; case Lwm2mSecurityType.X509: this.setValidatorsNoSecX509(); - this.lwm2mConfigFormGroup.get('client.cert').enable(); + this.lwm2mConfigFormGroup.get('client.cert').enable({emitEvent: false}); break; case Lwm2mSecurityType.PSK: this.lenMaxKeyClient = LEN_MAX_PSK; this.setValidatorsPskRpk(mode); - this.lwm2mConfigFormGroup.get('client.identity').enable(); + this.lwm2mConfigFormGroup.get('client.identity').enable({emitEvent: false}); break; case Lwm2mSecurityType.RPK: this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK; this.setValidatorsPskRpk(mode); - this.lwm2mConfigFormGroup.get('client.identity').disable(); + this.lwm2mConfigFormGroup.get('client.identity').disable({emitEvent: false}); break; } this.lwm2mConfigFormGroup.get('client.identity').updateValueAndValidity({emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index d0ad42b62e..98b50cde33 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -110,7 +110,6 @@ import { RuleChainAutocompleteComponent } from '@home/components/rule-chain/rule import { DeviceProfileProvisionConfigurationComponent } from '@home/components/profile/device-profile-provision-configuration.component'; import { AlarmScheduleComponent } from '@home/components/profile/alarm/alarm-schedule.component'; import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component'; -import { DeviceCredentialsComponent } from '@home/components/device/device-credentials.component'; import { AlarmScheduleInfoComponent } from '@home/components/profile/alarm/alarm-schedule-info.component'; import { AlarmScheduleDialogComponent } from '@home/components/profile/alarm/alarm-schedule-dialog.component'; import { EditAlarmDetailsDialogComponent } from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; @@ -120,7 +119,6 @@ import { TenantProfileConfigurationComponent } from '@home/components/profile/te import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component'; import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component'; import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component'; -import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-device-credentials.component'; import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module'; import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component'; import { DashboardToolbarComponent } from '@home/components/dashboard-page/dashboard-toolbar.component'; @@ -138,11 +136,10 @@ 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'; import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component'; import { WidgetContainerComponent } from '@home/components/widget/widget-container.component'; import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module'; +import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module'; @NgModule({ declarations: @@ -243,10 +240,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic DeviceProfileProvisionConfigurationComponent, AlarmScheduleComponent, DeviceWizardDialogComponent, - DeviceCredentialsComponent, - CopyDeviceCredentialsComponent, - SecurityConfigLwm2mComponent, - SecurityConfigLwm2mServerComponent, AlarmScheduleDialogComponent, EditAlarmDetailsDialogComponent, SmsProviderConfigurationComponent, @@ -272,7 +265,8 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic SharedHomeComponentsModule, Lwm2mProfileComponentsModule, SnmpDeviceProfileTransportModule, - StatesControllerModule + StatesControllerModule, + DeviceCredentialsModule ], exports: [ EntitiesTableComponent, @@ -351,10 +345,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic AddDeviceProfileDialogComponent, RuleChainAutocompleteComponent, DeviceWizardDialogComponent, - DeviceCredentialsComponent, - CopyDeviceCredentialsComponent, - SecurityConfigLwm2mComponent, - SecurityConfigLwm2mServerComponent, AlarmScheduleInfoComponent, AlarmScheduleComponent, AlarmScheduleDialogComponent, diff --git a/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html index 8e1a67a8a9..c453493a2c 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html @@ -25,16 +25,26 @@ close - + -
+
-
- - -
+
+
+ + +
+
+ +
+ + + {{ 'device.loading-device-credentials' | translate }} + +
+