UI: Add 2FA general setting form

This commit is contained in:
Vladyslav_Prykhodko 2022-04-29 18:12:35 +03:00
parent d9a2495ea4
commit e29be2bded
7 changed files with 283 additions and 155 deletions

View File

@ -1,3 +1,19 @@
///
/// Copyright © 2016-2022 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 { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';

View File

@ -364,7 +364,7 @@ export class MenuService {
name: 'admin.system-settings', name: 'admin.system-settings',
type: 'toggle', type: 'toggle',
path: '/settings', path: '/settings',
height: '80px', height: '120px',
icon: 'settings', icon: 'settings',
pages: [ pages: [
{ {
@ -374,6 +374,14 @@ export class MenuService {
path: '/settings/home', path: '/settings/home',
icon: 'settings_applications' icon: 'settings_applications'
}, },
{
id: guid(),
name: 'admin.2fa.2fa',
type: 'link',
path: '/settings/2fa',
icon: 'mdi:two-factor-authentication',
isMdiIcon: true
},
{ {
id: guid(), id: guid(),
name: 'resource.resources-library', name: 'resource.resources-library',
@ -510,6 +518,12 @@ export class MenuService {
icon: 'settings_applications', icon: 'settings_applications',
path: '/settings/home' path: '/settings/home'
}, },
{
name: 'admin.2fa.2fa',
icon: 'mdi:two-factor-authentication',
isMdiIcon: true,
path: '/settings/2fa'
},
{ {
name: 'resource.resources-library', name: 'resource.resources-library',
icon: 'folder', icon: 'folder',

View File

@ -1,3 +1,20 @@
<!--
Copyright © 2016-2022 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> <div>
<mat-card class="settings-card"> <mat-card class="settings-card">
<mat-card-title> <mat-card-title>
@ -11,131 +28,146 @@
</mat-progress-bar> </mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<mat-card-content style="padding-top: 16px;"> <mat-card-content style="padding-top: 16px;">
<form [formGroup]="twoFaFormGroup" (ngSubmit)="save()" fxLayout="column"> <form [formGroup]="twoFaFormGroup" (ngSubmit)="save()">
<mat-checkbox *ngIf="isTenantAdmin()" formControlName="useSystemTwoFactorAuthSettings" style="padding-bottom: 16px;"> <fieldset [disabled]="isLoading$ | async">
{{ 'admin.2fa.use-system-two-factor-auth-settings' | translate }} <mat-checkbox *ngIf="isTenantAdmin()" formControlName="useSystemTwoFactorAuthSettings" style="padding-bottom: 16px;">
</mat-checkbox> {{ 'admin.2fa.use-system-two-factor-auth-settings' | translate }}
<ng-container *ngIf="!isTenantAdmin() || twoFaFormGroup.get('useSystemTwoFactorAuthSettings').value"> </mat-checkbox>
<mat-form-field> <ng-container *ngIf="!isTenantAdmin() || !twoFaFormGroup.get('useSystemTwoFactorAuthSettings').value">
<mat-label translate>admin.2fa.total-allowed-time-for-verification</mat-label> <mat-form-field fxFlex class="mat-block">
<input matInput required formControlName="totalAllowedTimeForVerification" type="number" step="1" min="1"> <mat-label translate>admin.2fa.total-allowed-time-for-verification</mat-label>
<mat-error *ngIf="twoFaFormGroup.get('totalAllowedTimeForVerification').hasError('required')"> <input matInput required formControlName="totalAllowedTimeForVerification" type="number" step="1" min="1">
{{ 'admin.2fa.total-allowed-time-for-verification-required' | translate }} <mat-error *ngIf="twoFaFormGroup.get('totalAllowedTimeForVerification').hasError('required')">
</mat-error> {{ 'admin.2fa.total-allowed-time-for-verification-required' | translate }}
<mat-error *ngIf="twoFaFormGroup.get('totalAllowedTimeForVerification').hasError('pattern') </mat-error>
|| twoFaFormGroup.get('totalAllowedTimeForVerification').hasError('min')"> <mat-error *ngIf="twoFaFormGroup.get('totalAllowedTimeForVerification').hasError('pattern')
{{ 'admin.2fa.total-allowed-time-for-verification-pattern' | translate }} || twoFaFormGroup.get('totalAllowedTimeForVerification').hasError('min')">
</mat-error> {{ 'admin.2fa.total-allowed-time-for-verification-pattern' | translate }}
</mat-form-field> </mat-error>
<mat-form-field> </mat-form-field>
<mat-label translate>admin.2fa.max-verification-failures-before-user-lockout</mat-label> <mat-form-field fxFlex class="mat-block">
<input matInput required formControlName="maxVerificationFailuresBeforeUserLockout" type="number" step="1" min="0" max="65535"> <mat-label translate>admin.2fa.max-verification-failures-before-user-lockout</mat-label>
<mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('required')"> <input matInput required formControlName="maxVerificationFailuresBeforeUserLockout" type="number" step="1" min="0" max="65535">
{{ 'admin.2fa.max-verification-failures-before-user-lockout-required' | translate }} <mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('required')">
</mat-error> {{ 'admin.2fa.max-verification-failures-before-user-lockout-required' | translate }}
<mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('pattern') </mat-error>
|| twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('min') <mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('pattern')
|| twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('max')"> || twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('min')
{{ 'admin.2fa.max-verification-failures-before-user-lockout-pattern' | translate }} || twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('max')">
</mat-error> {{ 'admin.2fa.max-verification-failures-before-user-lockout-pattern' | translate }}
</mat-form-field> </mat-error>
<mat-form-field> </mat-form-field>
<mat-label translate>admin.2fa.verification-code-send-rate-limit</mat-label> <mat-form-field fxFlex class="mat-block">
<input matInput formControlName="verificationCodeSendRateLimit"> <mat-label translate>admin.2fa.verification-code-send-rate-limit</mat-label>
<mat-hint translate>admin.2fa.verification-code-send-rate-limit-hint</mat-hint> <input matInput formControlName="verificationCodeSendRateLimit" required>
<mat-error *ngIf="twoFaFormGroup.get('verificationCodeSendRateLimit').hasError('pattern')"> <mat-error *ngIf="twoFaFormGroup.get('verificationCodeSendRateLimit').hasError('required')">
{{ 'admin.2fa.verification-code-send-rate-limit-pattern' | translate }} {{ 'admin.2fa.verification-code-send-rate-limit-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> <mat-error *ngIf="twoFaFormGroup.get('verificationCodeSendRateLimit').hasError('pattern')">
<mat-form-field> {{ 'admin.2fa.verification-code-send-rate-limit-pattern' | translate }}
<mat-label translate>admin.2fa.verification-code-check-rate-limit</mat-label> </mat-error>
<input matInput formControlName="verificationCodeCheckRateLimit"> </mat-form-field>
<mat-hint translate>admin.2fa.verification-code-check-rate-limit-hint</mat-hint> <mat-form-field fxFlex class="mat-block">
<mat-error *ngIf="twoFaFormGroup.get('verificationCodeCheckRateLimit').hasError('pattern')"> <mat-label translate>admin.2fa.verification-code-check-rate-limit</mat-label>
{{ 'admin.2fa.verification-code-check-rate-limit-pattern' | translate }} <input matInput formControlName="verificationCodeCheckRateLimit" required>
</mat-error> <mat-error *ngIf="twoFaFormGroup.get('verificationCodeCheckRateLimit').hasError('required')">
</mat-form-field> {{ 'admin.2fa.verification-code-check-rate-limit-required' | translate }}
<div class="mat-h3">Providers</div> </mat-error>
<ng-container formArrayName="providers"> <mat-error *ngIf="twoFaFormGroup.get('verificationCodeCheckRateLimit').hasError('pattern')">
<div class="container"> {{ 'admin.2fa.verification-code-check-rate-limit-pattern' | translate }}
<mat-expansion-panel *ngFor="let provider of providersForm.controls; let j = index; trackBy: trackByParams" </mat-error>
class="registration-card mat-elevation-z0"> </mat-form-field>
<mat-expansion-panel-header> <div class="mat-h3" translate>admin.2fa.available-providers</div>
<mat-panel-title fxLayoutAlign="start center"> <ng-container formArrayName="providers">
{{ provider.value.providerType }} <div class="container">
</mat-panel-title> <mat-accordion multi>
<mat-panel-description fxLayoutAlign="end center"> <mat-expansion-panel *ngFor="let provider of providersForm.controls; let i = index">
<button mat-icon-button <mat-expansion-panel-header>
type="button" <mat-panel-title fxLayoutAlign="start center">
[disabled]="providersForm.controls.length < 2" {{ provider.value.providerType }}
(click)="removeProviders($event, j)" </mat-panel-title>
matTooltip="{{ 'admin.oauth2.delete-provider' | translate }}" <mat-panel-description fxLayoutAlign="end center">
matTooltipPosition="above"> <button mat-icon-button
<mat-icon>delete</mat-icon> type="button"
</button> (click)="removeProviders($event, i)"
</mat-panel-description> matTooltip="{{ 'admin.oauth2.delete-provider' | translate }}"
</mat-expansion-panel-header> matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<section [formGroupName]="j"> <section [formGroupName]="i">
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<section fxFlex formGroupName="additionalInfo" fxLayout="row">
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.oauth2.login-provider</mat-label> <mat-label translate>admin.2fa.provider</mat-label>
<mat-select formControlName="providerName"> <mat-select formControlName="providerType">
<mat-option *ngFor="let provider of templateProvider" [value]="provider"> <mat-option *ngFor="let twoFactorAuthProviderType of twoFactorAuthProviderTypes"
{{ provider }} [value]="twoFactorAuthProviderType"
[disabled]="selectedTypes(twoFactorAuthProviderType[twoFactorAuthProviderType], i)">
{{ twoFactorAuthProviderType }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<ng-container [ngSwitch]="provider.get('providerType').value">
<ng-container *ngSwitchCase="twoFactorAuthProviderType.TOTP">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.issuer-name</mat-label>
<input matInput formControlName="issuerName" required>
<mat-error *ngIf="provider.get('issuerName').hasError('required')">
{{ "admin.2fa.issuer-name-required" | translate }}
</mat-error>
</mat-form-field>
</ng-container>
<div *ngSwitchCase="twoFactorAuthProviderType.SMS"
fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.verification-message-template</mat-label>
<input matInput formControlName="smsVerificationMessageTemplate" required>
<mat-error *ngIf="provider.get('smsVerificationMessageTemplate').hasError('required')">
{{ "admin.2fa.verification-message-template-required" | translate }}
</mat-error>
<mat-error *ngIf="provider.get('smsVerificationMessageTemplate').hasError('pattern')">
{{ "admin.2fa.verification-message-template-pattern" | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.verification-code-lifetime</mat-label>
<input matInput formControlName="verificationCodeLifetime" type="number" step="1" min="1" required>
<mat-error *ngIf="provider.get('verificationCodeLifetime').hasError('required')">
{{ "admin.2fa.verification-code-lifetime-required" | translate }}
</mat-error>
<mat-error *ngIf="provider.get('verificationCodeLifetime').hasError('min') ||
provider.get('verificationCodeLifetime').hasError('pattern')">
{{ "admin.2fa.verification-code-lifetime-pattern" | translate }}
</mat-error>
</mat-form-field>
</div>
</ng-container>
</section> </section>
<mat-form-field floatLabel="always" fxFlex class="mat-block"> </ng-template>
<mat-label translate>admin.oauth2.allowed-platforms</mat-label> </mat-expansion-panel>
<mat-select formControlName="platforms" multiple placeholder="{{ 'admin.oauth2.all-platforms' | translate }}"> </mat-accordion>
<mat-option *ngFor="let platform of platformTypes" [value]="platform"> </div>
{{ platformTypeTranslations.get(platform) | translate }} </ng-container>
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.oauth2.client-id</mat-label>
<input matInput formControlName="clientId" required>
<mat-error *ngIf="registration.get('clientId').hasError('required')">
{{ 'admin.oauth2.client-id-required' | translate }}
</mat-error>
<mat-error *ngIf="registration.get('clientId').hasError('maxlength')">
{{ 'admin.oauth2.client-id-max-length' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.oauth2.client-secret</mat-label>
<input matInput formControlName="clientSecret" required>
<mat-error *ngIf="registration.get('clientSecret').hasError('required')">
{{ 'admin.oauth2.client-secret-required' | translate }}
</mat-error>
<mat-error *ngIf="registration.get('clientSecret').hasError('maxlength')">
{{ 'admin.oauth2.client-secret-max-length' | translate }}
</mat-error>
</mat-form-field>
</div>
</section>
</ng-template>
</mat-expansion-panel>
</div>
</ng-container> </ng-container>
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
</ng-container> <button type="button" mat-raised-button color="primary"
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> [disabled]="twoFaFormGroup.get('useSystemTwoFactorAuthSettings').value
<button mat-button mat-raised-button color="primary" || providersForm.length == twoFactorAuthProviderTypes.length || (isLoading$ | async)"
[disabled]="(isLoading$ | async) || twoFaFormGroup.invalid || !twoFaFormGroup.dirty" (click)="addProvider()">
type="submit"> <mat-icon>add</mat-icon>
{{'action.save' | translate}} <span translate>action.add</span>
</button> </button>
</div> <button mat-button mat-raised-button color="primary"
[disabled]="(isLoading$ | async) || twoFaFormGroup.invalid || !twoFaFormGroup.dirty"
type="submit">
{{'action.save' | translate}}
</button>
</div>
</fieldset>
</form> </form>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>

View File

@ -0,0 +1,21 @@
/**
* Copyright © 2016-2022 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{
.container {
margin-bottom: 1em;
}
}

View File

@ -1,10 +1,26 @@
///
/// Copyright © 2016-2022 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 { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { WINDOW } from '@core/services/window.service'; import { WINDOW } from '@core/services/window.service';
@ -12,11 +28,7 @@ import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentica
import { AuthState } from '@core/auth/auth.models'; import { AuthState } from '@core/auth/auth.models';
import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { Authority } from '@shared/models/authority.enum'; import { Authority } from '@shared/models/authority.enum';
import { import { TwoFactorAuthProviderType, TwoFactorAuthSettings } from '@shared/models/two-factor-auth.models';
TwoFactorAuthProviderType,
TwoFactorAuthSettings,
TwoFactorAuthSettingsForm
} from '@shared/models/two-factor-auth.models';
@Component({ @Component({
selector: 'tb-2fa-settings', selector: 'tb-2fa-settings',
@ -29,6 +41,8 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
private authUser = this.authState.authUser; private authUser = this.authState.authUser;
twoFaFormGroup: FormGroup; twoFaFormGroup: FormGroup;
twoFactorAuthProviderTypes = Object.keys(TwoFactorAuthProviderType);
twoFactorAuthProviderType = TwoFactorAuthProviderType;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private route: ActivatedRoute, private route: ActivatedRoute,
@ -43,7 +57,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
ngOnInit() { ngOnInit() {
this.build2faSettingsForm(); this.build2faSettingsForm();
this.twoFaService.getTwoFaSettings().subscribe((setting) => { this.twoFaService.getTwoFaSettings().subscribe((setting) => {
console.log(this.formDataPreprocessing(setting)); this.initTwoFactorAuthForm(setting);
}); });
} }
@ -60,12 +74,19 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
} }
save() { save() {
const setting = this.twoFaFormGroup.value;
this.twoFaService.saveTwoFaSettings(setting).subscribe(
(twoFactorAuthSettings) => {
this.twoFaFormGroup.patchValue(twoFactorAuthSettings, {emitEvent: false});
this.twoFaFormGroup.markAsUntouched();
this.twoFaFormGroup.markAsPristine();
}
);
} }
private build2faSettingsForm(): void { private build2faSettingsForm(): void {
this.twoFaFormGroup = this.fb.group({ this.twoFaFormGroup = this.fb.group({
useSystemTwoFactorAuthSettings: [false], useSystemTwoFactorAuthSettings: [this.isTenantAdmin()],
maxVerificationFailuresBeforeUserLockout: [30, [ maxVerificationFailuresBeforeUserLockout: [30, [
Validators.required, Validators.required,
Validators.pattern(/^\d*$/), Validators.pattern(/^\d*$/),
@ -77,13 +98,20 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
Validators.min(1), Validators.min(1),
Validators.pattern(/^\d*$/) Validators.pattern(/^\d*$/)
]], ]],
verificationCodeCheckRateLimit: ['', Validators.pattern(/^[1-9]\d*:[1-9]\d*$/)], verificationCodeCheckRateLimit: ['3:900', [Validators.required, Validators.pattern(/^[1-9]\d*:[1-9]\d*$/)]],
verificationCodeSendRateLimit: ['', Validators.pattern(/^[1-9]\d*:[1-9]\d*$/)], verificationCodeSendRateLimit: ['1:60', [Validators.required, Validators.pattern(/^[1-9]\d*:[1-9]\d*$/)]],
providers: this.fb.array([]) providers: this.fb.array([])
}); });
} }
addProviders() { private initTwoFactorAuthForm(settings: TwoFactorAuthSettings) {
settings.providers.forEach(() => {
this.addProvider();
});
this.twoFaFormGroup.patchValue(settings, {emitEvent: false});
}
addProvider() {
const newProviders = this.fb.group({ const newProviders = this.fb.group({
providerType: [TwoFactorAuthProviderType.TOTP], providerType: [TwoFactorAuthProviderType.TOTP],
issuerName: ['', Validators.required], issuerName: ['', Validators.required],
@ -119,8 +147,9 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
}); });
if (this.providersForm.length) { if (this.providersForm.length) {
const selectProvidersType = this.providersForm.value[0].providerType; const selectProvidersType = this.providersForm.value[0].providerType;
if (selectProvidersType !== TwoFactorAuthProviderType.TOTP) { if (selectProvidersType === TwoFactorAuthProviderType.TOTP) {
newProviders.get('providerType').patchValue(TwoFactorAuthProviderType.SMS, {emitEvents: true}) newProviders.get('providerType').setValue(TwoFactorAuthProviderType.SMS);
newProviders.updateValueAndValidity();
} }
} }
this.providersForm.push(newProviders); this.providersForm.push(newProviders);
@ -140,16 +169,10 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
return this.twoFaFormGroup.get('providers') as FormArray; return this.twoFaFormGroup.get('providers') as FormArray;
} }
private formDataPreprocessing(data: TwoFactorAuthSettings): TwoFactorAuthSettingsForm { selectedTypes(type: TwoFactorAuthProviderType, index: number): boolean {
return data; const selectedProviderTypes: TwoFactorAuthProviderType[] = this.providersForm.value.map(providers => providers.providerType);
} selectedProviderTypes.splice(index, 1);
return selectedProviderTypes.includes(type);
private formDataPostprocessing(data: TwoFactorAuthSettingsForm): TwoFactorAuthSettings{
return data;
}
trackByParams(index: number): number {
return index;
} }
} }

View File

@ -1,3 +1,19 @@
///
/// Copyright © 2016-2022 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.
///
export interface TwoFactorAuthSettings { export interface TwoFactorAuthSettings {
maxVerificationFailuresBeforeUserLockout: number; maxVerificationFailuresBeforeUserLockout: number;
providers: Array<TwoFactorAuthProviderConfig>; providers: Array<TwoFactorAuthProviderConfig>;
@ -7,7 +23,7 @@ export interface TwoFactorAuthSettings {
verificationCodeSendRateLimit: string; verificationCodeSendRateLimit: string;
} }
export type TwoFactorAuthProviderConfig = Partial<TotpTwoFactorAuthProviderConfig | SmsTwoFactorAuthProviderConfig> export type TwoFactorAuthProviderConfig = Partial<TotpTwoFactorAuthProviderConfig | SmsTwoFactorAuthProviderConfig>;
export interface TotpTwoFactorAuthProviderConfig { export interface TotpTwoFactorAuthProviderConfig {
providerType: TwoFactorAuthProviderType; providerType: TwoFactorAuthProviderType;
@ -24,7 +40,3 @@ export enum TwoFactorAuthProviderType{
TOTP = 'TOTP', TOTP = 'TOTP',
SMS = 'SMS' SMS = 'SMS'
} }
export interface TwoFactorAuthSettingsForm extends TwoFactorAuthSettings {
}

View File

@ -255,19 +255,29 @@
}, },
"2fa": { "2fa": {
"2fa": "Two-factor authentication", "2fa": "Two-factor authentication",
"use-system-two-factor-auth-settings": "Use system two factor auth settings", "available-providers": "Available providers:",
"total-allowed-time-for-verification": "Total allowed time for verification", "issuer-name": "Issuer name",
"total-allowed-time-for-verification-required": "Total allowed time is required.", "issuer-name-required": "Issuer name is required.",
"total-allowed-time-for-verification-pattern": "Total allowed time must be a positive integer.",
"max-verification-failures-before-user-lockout": "Max verification failures before user lockout", "max-verification-failures-before-user-lockout": "Max verification failures before user lockout",
"max-verification-failures-before-user-lockout-required": "Max verification failures is required.",
"max-verification-failures-before-user-lockout-pattern": "Max verification failures must be a positive integer.", "max-verification-failures-before-user-lockout-pattern": "Max verification failures must be a positive integer.",
"max-verification-failures-before-user-lockout-required": "Max verification failures is required.",
"provider": "Provider",
"total-allowed-time-for-verification": "Total allowed time for verification",
"total-allowed-time-for-verification-pattern": "Total allowed time must be a positive integer.",
"total-allowed-time-for-verification-required": "Total allowed time is required.",
"use-system-two-factor-auth-settings": "Use system two factor auth settings",
"verification-code-check-rate-limit": "Verification code check rate limit", "verification-code-check-rate-limit": "Verification code check rate limit",
"verification-code-check-rate-limit-hint": "If empty field, the limit not be apply",
"verification-code-check-rate-limit-pattern": "Verification code check limit has invalid format", "verification-code-check-rate-limit-pattern": "Verification code check limit has invalid format",
"verification-code-check-rate-limit-required": "Verification code check rate limit is required.",
"verification-code-lifetime": "Verification code lifetime",
"verification-code-lifetime-pattern": "Verification code lifetime must be a positive integer.",
"verification-code-lifetime-required": "Verification code lifetime is required.",
"verification-code-send-rate-limit": "Verification code send rate limit", "verification-code-send-rate-limit": "Verification code send rate limit",
"verification-code-send-rate-limit-hint": "If empty field, the limit not be apply", "verification-code-send-rate-limit-pattern": "Verification code send limit has invalid format",
"verification-code-send-rate-limit-pattern": "Verification code send limit has invalid format" "verification-code-send-rate-limit-required": "Verification code send rate limit is required.",
"verification-message-template": "Verification message template",
"verification-message-template-pattern": "Verification message need to contains pattern: ${verificationCode}",
"verification-message-template-required": "Verification message template is required."
} }
}, },
"alarm": { "alarm": {