Changed Security block for REST Connector
This commit is contained in:
parent
c4e5ab65ee
commit
580f3b5b3c
@ -0,0 +1,65 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<div class="tb-form-row space-between same-padding tb-flex column" [formGroup]="securityFormGroup">
|
||||||
|
<div class="tb-flex row space-between align-center no-gap fill-width">
|
||||||
|
<div class="fields-label" translate>gateway.security</div>
|
||||||
|
<tb-toggle-select formControlName="type" appearance="fill">
|
||||||
|
<tb-toggle-option *ngFor="let type of securityTypes" [value]="type">
|
||||||
|
{{ SecurityTypeTranslationsMap.get(type) | translate }}
|
||||||
|
</tb-toggle-option>
|
||||||
|
</tb-toggle-select>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="securityFormGroup.get('type').value === BrokerSecurityType.BASIC">
|
||||||
|
<div class="tb-form-row space-between tb-flex fill-width">
|
||||||
|
<div class="fixed-title-width" translate>gateway.username</div>
|
||||||
|
<div class="tb-flex no-gap">
|
||||||
|
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<input matInput name="value" formControlName="username" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||||
|
<mat-icon matSuffix
|
||||||
|
matTooltipPosition="above"
|
||||||
|
matTooltipClass="tb-error-tooltip"
|
||||||
|
[matTooltip]="('gateway.username-required') | translate"
|
||||||
|
*ngIf="securityFormGroup.get('username').hasError('required') && securityFormGroup.get('username').touched"
|
||||||
|
class="tb-error">
|
||||||
|
warning
|
||||||
|
</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tb-form-row space-between tb-flex fill-width">
|
||||||
|
<div class="fixed-title-width" translate>gateway.password</div>
|
||||||
|
<div class="tb-flex no-gap">
|
||||||
|
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<input matInput type="password" name="value" formControlName="password" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||||
|
<mat-icon matSuffix
|
||||||
|
matTooltipPosition="above"
|
||||||
|
matTooltipClass="tb-error-tooltip"
|
||||||
|
[matTooltip]="('gateway.password-required') | translate"
|
||||||
|
*ngIf="securityFormGroup.get('password').hasError('required')
|
||||||
|
&& securityFormGroup.get('password').touched"
|
||||||
|
class="tb-error">
|
||||||
|
warning
|
||||||
|
</mat-icon>
|
||||||
|
<div [class.hide-toggle]="securityFormGroup.get('password').hasError('required')" class="tb-flex no-gap align-center fill-height" matSuffix>
|
||||||
|
<tb-toggle-password class="tb-flex align-center fill-height"></tb-toggle-password>
|
||||||
|
</div>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2024 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 {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.fields-label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 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 {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
forwardRef,
|
||||||
|
OnDestroy,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
FormBuilder,
|
||||||
|
NG_VALIDATORS,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
UntypedFormGroup,
|
||||||
|
ValidationErrors,
|
||||||
|
Validator,
|
||||||
|
Validators
|
||||||
|
} from '@angular/forms';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
noLeadTrailSpacesRegex,
|
||||||
|
RestSecurityType,
|
||||||
|
RestSecurityTypeTranslationsMap
|
||||||
|
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-rest-connector-security',
|
||||||
|
templateUrl: './rest-connector-security.component.html',
|
||||||
|
styleUrls: ['./rest-connector-security.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => RestConnectorSecurityComponent),
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => RestConnectorSecurityComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
CommonModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class RestConnectorSecurityComponent implements ControlValueAccessor, Validator, OnDestroy {
|
||||||
|
BrokerSecurityType = RestSecurityType;
|
||||||
|
securityTypes: RestSecurityType[] = Object.values(RestSecurityType);
|
||||||
|
SecurityTypeTranslationsMap = RestSecurityTypeTranslationsMap;
|
||||||
|
securityFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
private propagateChange = (_: any) => {};
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {
|
||||||
|
this.securityFormGroup = this.fb.group({
|
||||||
|
type: [RestSecurityType.ANONYMOUS, []],
|
||||||
|
username: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
|
password: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
|
});
|
||||||
|
this.observeSecurityForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {}
|
||||||
|
|
||||||
|
writeValue(deviceInfo: any): void {
|
||||||
|
if (!deviceInfo.type) {
|
||||||
|
deviceInfo.type = RestSecurityType.ANONYMOUS;
|
||||||
|
}
|
||||||
|
this.securityFormGroup.reset(deviceInfo);
|
||||||
|
this.updateView(deviceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): ValidationErrors | null {
|
||||||
|
return this.securityFormGroup.valid ? null : {
|
||||||
|
securityForm: { valid: false }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateView(value: any): void {
|
||||||
|
this.propagateChange(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateValidators(type: RestSecurityType): void {
|
||||||
|
if (type === RestSecurityType.BASIC) {
|
||||||
|
this.securityFormGroup.get('username').enable({emitEvent: false});
|
||||||
|
this.securityFormGroup.get('password').enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.securityFormGroup.get('username').disable({emitEvent: false});
|
||||||
|
this.securityFormGroup.get('password').disable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private observeSecurityForm(): void {
|
||||||
|
this.securityFormGroup.valueChanges
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(value => this.updateView(value));
|
||||||
|
|
||||||
|
this.securityFormGroup.get('type').valueChanges
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(type => this.updateValidators(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -368,38 +368,7 @@
|
|||||||
{{ 'gateway.rpc.add-header' | translate }}
|
{{ 'gateway.rpc.add-header' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="fields border" fxLayout="column" fxLayoutGap="10px" formArrayName="security">
|
<tb-rest-connector-security [formControl]="commandForm.get('security')"></tb-rest-connector-security>
|
||||||
<span class="fields-label">{{ 'gateway.rpc.security' | translate }}</span>
|
|
||||||
<div class="border" fxLayout="column" fxLayoutGap="10px" *ngIf="getFormArrayControls('security').length">
|
|
||||||
<div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center">
|
|
||||||
<span fxFlex class="title">{{ 'gateway.rpc.security-name' | translate }}</span>
|
|
||||||
<span fxFlex class="title">{{ 'gateway.rpc.value' | translate }}</span>
|
|
||||||
<span fxFlex="30px"></span>
|
|
||||||
</div>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<div fxFlex fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"
|
|
||||||
*ngFor="let control of getFormArrayControls('security'); let i = index">
|
|
||||||
<ng-container [formGroupName]="i">
|
|
||||||
<mat-form-field appearance="outline" fxFlex>
|
|
||||||
<input matInput formControlName="securityName"/>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-form-field appearance="outline" fxFlex>
|
|
||||||
<input matInput formControlName="value" placeholder="anonymous"/>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-icon style="cursor:pointer;"
|
|
||||||
fxFlex="30px"
|
|
||||||
(click)="removeHTTPSecurity(i)"
|
|
||||||
matTooltip="{{ 'gateway.rpc.remove' | translate }}">delete
|
|
||||||
</mat-icon>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button mat-raised-button
|
|
||||||
fxFlexAlign="start"
|
|
||||||
(click)="addHTTPSecurity()">
|
|
||||||
{{ 'gateway.rpc.add-security' | translate }}
|
|
||||||
</button>
|
|
||||||
</fieldset>
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template [ngSwitchCase]="ConnectorType.REQUEST">
|
<ng-template [ngSwitchCase]="ConnectorType.REQUEST">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
|
|||||||
@ -114,17 +114,12 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue
|
|||||||
this.commandForm = this.connectorParamsFormGroupByType(this.connectorType);
|
this.commandForm = this.connectorParamsFormGroupByType(this.connectorType);
|
||||||
this.commandForm.valueChanges.subscribe(value => {
|
this.commandForm.valueChanges.subscribe(value => {
|
||||||
const httpHeaders = {};
|
const httpHeaders = {};
|
||||||
const security = {};
|
|
||||||
switch (this.connectorType) {
|
switch (this.connectorType) {
|
||||||
case ConnectorType.REST:
|
case ConnectorType.REST:
|
||||||
value.httpHeaders.forEach(data => {
|
value.httpHeaders.forEach(data => {
|
||||||
httpHeaders[data.headerName] = data.value;
|
httpHeaders[data.headerName] = data.value;
|
||||||
})
|
})
|
||||||
value.httpHeaders = httpHeaders;
|
value.httpHeaders = httpHeaders;
|
||||||
value.security.forEach(data => {
|
|
||||||
security[data.securityName] = data.value;
|
|
||||||
})
|
|
||||||
value.security = security;
|
|
||||||
break;
|
break;
|
||||||
case ConnectorType.REQUEST:
|
case ConnectorType.REQUEST:
|
||||||
value.httpHeaders.forEach(data => {
|
value.httpHeaders.forEach(data => {
|
||||||
@ -252,7 +247,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue
|
|||||||
tries: [null, [Validators.required, Validators.min(1), Validators.pattern(this.numbersOnlyPattern)]],
|
tries: [null, [Validators.required, Validators.min(1), Validators.pattern(this.numbersOnlyPattern)]],
|
||||||
valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
httpHeaders: this.fb.array([]),
|
httpHeaders: this.fb.array([]),
|
||||||
security: this.fb.array([])
|
security: [{}, [Validators.required]]
|
||||||
})
|
})
|
||||||
break;
|
break;
|
||||||
case ConnectorType.REQUEST:
|
case ConnectorType.REQUEST:
|
||||||
@ -312,22 +307,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue
|
|||||||
oidsFA.removeAt(index);
|
oidsFA.removeAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
addHTTPSecurity(value: { securityName: string, value: string } = {securityName: null, value: null}) {
|
|
||||||
const securityFA = this.commandForm.get('security') as FormArray;
|
|
||||||
const formGroup = this.fb.group({
|
|
||||||
securityName: [value.securityName, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
|
||||||
value: [value.value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]]
|
|
||||||
})
|
|
||||||
if (securityFA) {
|
|
||||||
securityFA.push(formGroup, {emitEvent: false});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeHTTPSecurity(index: number) {
|
|
||||||
const oidsFA = this.commandForm.get('security') as FormArray;
|
|
||||||
oidsFA.removeAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFormArrayControls(path: string) {
|
getFormArrayControls(path: string) {
|
||||||
return (this.commandForm.get(path) as FormArray).controls as FormControl[];
|
return (this.commandForm.get(path) as FormArray).controls as FormControl[];
|
||||||
}
|
}
|
||||||
@ -402,11 +381,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue
|
|||||||
break;
|
break;
|
||||||
case ConnectorType.REST:
|
case ConnectorType.REST:
|
||||||
this.clearFromArrayByName("httpHeaders");
|
this.clearFromArrayByName("httpHeaders");
|
||||||
this.clearFromArrayByName("security");
|
|
||||||
value.security && Object.entries(value.security).forEach(securityHeader => {
|
|
||||||
this.addHTTPSecurity({securityName: securityHeader[0], value: securityHeader[1] as string})
|
|
||||||
})
|
|
||||||
delete value.security;
|
|
||||||
value.httpHeaders && Object.entries(value.httpHeaders).forEach(httpHeader => {
|
value.httpHeaders && Object.entries(value.httpHeaders).forEach(httpHeader => {
|
||||||
this.addHTTPHeader({headerName: httpHeader[0], value: httpHeader[1] as string})
|
this.addHTTPHeader({headerName: httpHeader[0], value: httpHeader[1] as string})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -360,6 +360,18 @@ export const BrokerSecurityTypeTranslationsMap = new Map<BrokerSecurityType, str
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export enum RestSecurityType {
|
||||||
|
ANONYMOUS = 'anonymous',
|
||||||
|
BASIC = 'basic',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RestSecurityTypeTranslationsMap = new Map<RestSecurityType, string>(
|
||||||
|
[
|
||||||
|
[RestSecurityType.ANONYMOUS, 'gateway.broker.security-types.anonymous'],
|
||||||
|
[RestSecurityType.BASIC, 'gateway.broker.security-types.basic'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
export const MqttVersions = [
|
export const MqttVersions = [
|
||||||
{ name: 3.1, value: 3 },
|
{ name: 3.1, value: 3 },
|
||||||
{ name: 3.11, value: 4 },
|
{ name: 3.11, value: 4 },
|
||||||
|
|||||||
@ -100,6 +100,7 @@ import { BarChartWidgetComponent } from '@home/components/widget/lib/chart/bar-c
|
|||||||
import { PolarAreaWidgetComponent } from '@home/components/widget/lib/chart/polar-area-widget.component';
|
import { PolarAreaWidgetComponent } from '@home/components/widget/lib/chart/polar-area-widget.component';
|
||||||
import { RadarChartWidgetComponent } from '@home/components/widget/lib/chart/radar-chart-widget.component';
|
import { RadarChartWidgetComponent } from '@home/components/widget/lib/chart/radar-chart-widget.component';
|
||||||
import { MobileAppQrcodeWidgetComponent } from '@home/components/widget/lib/mobile-app-qrcode-widget.component';
|
import { MobileAppQrcodeWidgetComponent } from '@home/components/widget/lib/mobile-app-qrcode-widget.component';
|
||||||
|
import { RestConnectorSecurityComponent } from '@home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations:
|
declarations:
|
||||||
@ -168,13 +169,14 @@ import { MobileAppQrcodeWidgetComponent } from '@home/components/widget/lib/mobi
|
|||||||
PolarAreaWidgetComponent,
|
PolarAreaWidgetComponent,
|
||||||
RadarChartWidgetComponent
|
RadarChartWidgetComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RpcWidgetsModule,
|
RpcWidgetsModule,
|
||||||
HomePageWidgetsModule,
|
HomePageWidgetsModule,
|
||||||
SharedHomeComponentsModule
|
SharedHomeComponentsModule,
|
||||||
],
|
RestConnectorSecurityComponent
|
||||||
|
],
|
||||||
exports: [
|
exports: [
|
||||||
EntitiesTableWidgetComponent,
|
EntitiesTableWidgetComponent,
|
||||||
AlarmsTableWidgetComponent,
|
AlarmsTableWidgetComponent,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user