mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 11:04:25 +00:00
feat(console): u2f (#1080)
* fix user table count * grpc ge * move grpc * u2f * add u2f funcs * rm local grpc, u2f dialog * dialog u2f * 2fa button * mfa u2f credentialoptions * decode base64 to bytearray, id, challenge * u2f verify * spinner, remove, attribute col * delete mfa * add forcemfa to policy * add id to remove * fix: add missing remove u2f in management * user mgmt u2f delete, login policy * rm log * show attr in mgmt user mfa * add missing id of mfa * mfa table * multifaktor for admin, org * add secondfactor to gen component * remove circular dependency * lint * revert identity prov * add divider * login policy lint * Update console/src/app/modules/policies/login-policy/login-policy.component.html * Update console/src/app/modules/policies/login-policy/login-policy.component.html Co-authored-by: Maximilian Peintner <csaq7175@uibk.ac.at> Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
cd44213e99
commit
c6fed8ae86
@ -0,0 +1,19 @@
|
|||||||
|
<h1 mat-dialog-title class="title"><span>{{data.title | translate}}</span></h1>
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<p class="desc">{{data.desc | translate}}</p>
|
||||||
|
|
||||||
|
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||||
|
<cnsl-label>{{'MFA.TYPE' | translate}}</cnsl-label>
|
||||||
|
<mat-select [(ngModel)]="newMfaType">
|
||||||
|
<mat-option *ngFor="let mfa of availableMfaTypes" [value]="mfa">
|
||||||
|
{{(data.componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</cnsl-form-field>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions class="action">
|
||||||
|
<button mat-button (click)="closeDialog()"><span translate>ACTIONS.CLOSE</span></button>
|
||||||
|
<button mat-raised-button class="ok-button" color="primary" (click)="closeDialogWithCode()"><span
|
||||||
|
translate>ACTIONS.OK</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -0,0 +1,22 @@
|
|||||||
|
.title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: .5rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
import { MultiFactorType as AdminMultiFactorType } from 'src/app/proto/generated/admin_pb';
|
||||||
|
import { MultiFactorType as MgmtMultiFactorType } from 'src/app/proto/generated/management_pb';
|
||||||
|
|
||||||
|
enum LoginMethodComponentType {
|
||||||
|
MultiFactor = 1,
|
||||||
|
SecondFactor = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dialog-add-type',
|
||||||
|
templateUrl: './dialog-add-type.component.html',
|
||||||
|
styleUrls: ['./dialog-add-type.component.scss'],
|
||||||
|
})
|
||||||
|
export class DialogAddTypeComponent {
|
||||||
|
public LoginMethodComponentType: any = LoginMethodComponentType;
|
||||||
|
public newMfaType!: AdminMultiFactorType | MgmtMultiFactorType;
|
||||||
|
public availableMfaTypes: Array<AdminMultiFactorType | MgmtMultiFactorType> = [];
|
||||||
|
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||||
|
this.availableMfaTypes = data.types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialogWithCode(): void {
|
||||||
|
this.dialogRef.close(this.newMfaType);
|
||||||
|
}
|
||||||
|
}
|
15
console/src/app/modules/mfa-table/mfa-table.component.html
Normal file
15
console/src/app/modules/mfa-table/mfa-table.component.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<div class="sp_wrapper">
|
||||||
|
<mat-spinner diameter="30" *ngIf="loading$ | async"></mat-spinner>
|
||||||
|
</div>
|
||||||
|
<div class="mfa-list">
|
||||||
|
<div class="mfa" *ngFor="let mfa of mfas">
|
||||||
|
<button [disabled]="disabled" mat-icon-button (click)="removeMfa(mfa)" class="rm">
|
||||||
|
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">
|
||||||
|
remove_circle</mat-icon>
|
||||||
|
</button>
|
||||||
|
{{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||||
|
</div>
|
||||||
|
<div class="mfa" (click)="addMfa()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
42
console/src/app/modules/mfa-table/mfa-table.component.scss
Normal file
42
console/src/app/modules/mfa-table/mfa-table.component.scss
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
.t .sp_wrapper {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfa-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0 -.5rem;
|
||||||
|
|
||||||
|
.mfa {
|
||||||
|
border: 1px solid var(--grey);
|
||||||
|
border-radius: .5rem;
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: .5rem;
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
min-height: 70px;
|
||||||
|
min-width: 150px;
|
||||||
|
|
||||||
|
.rm {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.disabled) {
|
||||||
|
&:hover {
|
||||||
|
background-color: #ffffff10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MfaTableComponent } from './mfa-table.component';
|
||||||
|
|
||||||
|
describe('MfaTableComponent', () => {
|
||||||
|
let component: MfaTableComponent;
|
||||||
|
let fixture: ComponentFixture<MfaTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [MfaTableComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MfaTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
220
console/src/app/modules/mfa-table/mfa-table.component.ts
Normal file
220
console/src/app/modules/mfa-table/mfa-table.component.ts
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import {
|
||||||
|
MultiFactor as AdminMultiFactor, MultiFactorType as AdminMultiFactorType,
|
||||||
|
SecondFactor as AdminSecondFactor, SecondFactorType as AdminSecondFactorType,
|
||||||
|
} from 'src/app/proto/generated/admin_pb';
|
||||||
|
import {
|
||||||
|
MultiFactor as MgmtMultiFactor, MultiFactorType as MgmtMultiFactorType,
|
||||||
|
SecondFactor as MgmtSecondFactor, SecondFactorType as MgmtSecondFactorType,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
|
||||||
|
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
|
||||||
|
import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component';
|
||||||
|
|
||||||
|
export enum LoginMethodComponentType {
|
||||||
|
MultiFactor = 1,
|
||||||
|
SecondFactor = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-mfa-table',
|
||||||
|
templateUrl: './mfa-table.component.html',
|
||||||
|
styleUrls: ['./mfa-table.component.scss'],
|
||||||
|
})
|
||||||
|
export class MfaTableComponent implements OnInit {
|
||||||
|
public LoginMethodComponentType: any = LoginMethodComponentType;
|
||||||
|
@Input() componentType!: LoginMethodComponentType;
|
||||||
|
@Input() public serviceType!: PolicyComponentServiceType;
|
||||||
|
@Input() service!: AdminService | ManagementService;
|
||||||
|
@Input() disabled: boolean = false;
|
||||||
|
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||||
|
public mfas: Array<AdminMultiFactorType | MgmtMultiFactorType | MgmtSecondFactorType | AdminSecondFactorType> = [];
|
||||||
|
|
||||||
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
|
|
||||||
|
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||||
|
|
||||||
|
constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { }
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeMfa(type: MgmtMultiFactorType | AdminMultiFactorType | MgmtSecondFactorType | AdminSecondFactorType): void {
|
||||||
|
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||||
|
data: {
|
||||||
|
confirmKey: 'ACTIONS.DELETE',
|
||||||
|
cancelKey: 'ACTIONS.CANCEL',
|
||||||
|
titleKey: 'MFA.DELETE.TITLE',
|
||||||
|
descriptionKey: 'MFA.DELETE.DESCRIPTION',
|
||||||
|
},
|
||||||
|
width: '400px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(resp => {
|
||||||
|
if (resp) {
|
||||||
|
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||||
|
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
||||||
|
const req = new MgmtMultiFactor();
|
||||||
|
req.setMultiFactor(type as MgmtMultiFactorType);
|
||||||
|
(this.service as ManagementService).RemoveMultiFactorFromLoginPolicy(req).then(() => {
|
||||||
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
});
|
||||||
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
|
const req = new MgmtSecondFactor();
|
||||||
|
req.setSecondFactor(type as MgmtSecondFactorType);
|
||||||
|
(this.service as ManagementService).RemoveSecondFactorFromLoginPolicy(req).then(() => {
|
||||||
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||||
|
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
||||||
|
const req = new AdminMultiFactor();
|
||||||
|
req.setMultiFactor(type as AdminMultiFactorType);
|
||||||
|
(this.service as AdminService).RemoveMultiFactorFromDefaultLoginPolicy(req).then(() => {
|
||||||
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
});
|
||||||
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
|
const req = new AdminSecondFactor();
|
||||||
|
req.setSecondFactor(type as AdminSecondFactorType);
|
||||||
|
(this.service as AdminService).RemoveSecondFactorFromDefaultLoginPolicy(req).then(() => {
|
||||||
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMfa(): void {
|
||||||
|
|
||||||
|
let selection: any[] = [];
|
||||||
|
|
||||||
|
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
||||||
|
selection = this.serviceType === PolicyComponentServiceType.MGMT ?
|
||||||
|
[MgmtMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] :
|
||||||
|
this.serviceType === PolicyComponentServiceType.ADMIN ?
|
||||||
|
[AdminMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] :
|
||||||
|
[];
|
||||||
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
|
selection = this.serviceType === PolicyComponentServiceType.MGMT ?
|
||||||
|
[MgmtSecondFactorType.SECONDFACTORTYPE_U2F, MgmtSecondFactorType.SECONDFACTORTYPE_U2F] :
|
||||||
|
this.serviceType === PolicyComponentServiceType.ADMIN ?
|
||||||
|
[AdminSecondFactorType.SECONDFACTORTYPE_OTP, AdminSecondFactorType.SECONDFACTORTYPE_U2F] :
|
||||||
|
[];
|
||||||
|
}
|
||||||
|
const dialogRef = this.dialog.open(DialogAddTypeComponent, {
|
||||||
|
data: {
|
||||||
|
title: 'MFA.CREATE.TITLE',
|
||||||
|
desc: 'MFA.CREATE.DESCRIPTION',
|
||||||
|
componentType: this.componentType,
|
||||||
|
types: selection,
|
||||||
|
},
|
||||||
|
width: '400px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe((mfaType: AdminMultiFactorType | MgmtMultiFactorType |
|
||||||
|
AdminSecondFactorType | MgmtSecondFactorType) => {
|
||||||
|
if (mfaType) {
|
||||||
|
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||||
|
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
||||||
|
const req = new MgmtMultiFactor();
|
||||||
|
req.setMultiFactor(mfaType as MgmtMultiFactorType);
|
||||||
|
(this.service as ManagementService).AddMultiFactorToLoginPolicy(req).then(() => {
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
|
const req = new MgmtSecondFactor();
|
||||||
|
req.setSecondFactor(mfaType as MgmtSecondFactorType);
|
||||||
|
(this.service as ManagementService).AddSecondFactorToLoginPolicy(req).then(() => {
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||||
|
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
||||||
|
const req = new AdminMultiFactor();
|
||||||
|
req.setMultiFactor(mfaType as AdminMultiFactorType);
|
||||||
|
(this.service as AdminService).addMultiFactorToDefaultLoginPolicy(req).then(() => {
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
|
const req = new AdminSecondFactor();
|
||||||
|
req.setSecondFactor(mfaType as AdminSecondFactorType);
|
||||||
|
(this.service as AdminService).AddSecondFactorToDefaultLoginPolicy(req).then(() => {
|
||||||
|
this.refreshPageAfterTimout(2000);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData(): Promise<void> {
|
||||||
|
this.loadingSubject.next(true);
|
||||||
|
|
||||||
|
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||||
|
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
||||||
|
(this.service as ManagementService).GetLoginPolicyMultiFactors().then(resp => {
|
||||||
|
this.mfas = resp.toObject().multiFactorsList;
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
});
|
||||||
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
|
(this.service as ManagementService).GetLoginPolicySecondFactors().then(resp => {
|
||||||
|
this.mfas = resp.toObject().secondFactorsList;
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||||
|
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
||||||
|
(this.service as AdminService).getDefaultLoginPolicyMultiFactors().then(resp => {
|
||||||
|
this.mfas = resp.toObject().multiFactorsList;
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
});
|
||||||
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
|
(this.service as AdminService).GetDefaultLoginPolicySecondFactors().then(resp => {
|
||||||
|
this.mfas = resp.toObject().secondFactorsList;
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshPageAfterTimout(to: number): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getData();
|
||||||
|
}, to);
|
||||||
|
}
|
||||||
|
}
|
44
console/src/app/modules/mfa-table/mfa-table.module.ts
Normal file
44
console/src/app/modules/mfa-table/mfa-table.module.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||||
|
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||||
|
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||||
|
import { TruncatePipeModule } from 'src/app/pipes/truncate-pipe/truncate-pipe.module';
|
||||||
|
|
||||||
|
import { MfaTableComponent } from './mfa-table.component';
|
||||||
|
import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component';
|
||||||
|
import { InputModule } from '../input/input.module';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MfaTableComponent, DialogAddTypeComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
InputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
TranslateModule,
|
||||||
|
TimestampToDatePipeModule,
|
||||||
|
HasRoleModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
MfaTableComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class MfaTableModule { }
|
@ -44,8 +44,34 @@
|
|||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
<p> {{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}} </p>
|
<p> {{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}} </p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
|
||||||
|
[(ngModel)]="loginData.forceMfa">
|
||||||
|
{{'POLICY.DATA.FORCEMFA' | translate}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
<p> {{'POLICY.DATA.FORCEMFA_DESC' | translate}} </p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button [disabled]="disabled" class="save-button" (click)="savePolicy()" color="primary" type="submit"
|
||||||
|
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<h3 class="subheader">{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}</h3>
|
||||||
|
<p class="subdesc">{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}</p>
|
||||||
|
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||||
|
[componentType]="LoginMethodComponentType.MultiFactor"
|
||||||
|
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.policy.write' : ''] | hasRole | async) == false">
|
||||||
|
</app-mfa-table>
|
||||||
|
|
||||||
|
<h3 class="subheader">{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}</h3>
|
||||||
|
<p class="subdesc">{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}</p>
|
||||||
|
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||||
|
[componentType]="LoginMethodComponentType.SecondFactor"
|
||||||
|
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.policy.write' : ''] | hasRole | async) == false">
|
||||||
|
</app-mfa-table>
|
||||||
|
|
||||||
<h3 class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</h3>
|
<h3 class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</h3>
|
||||||
|
|
||||||
<div class="idps">
|
<div class="idps">
|
||||||
@ -63,9 +89,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button [disabled]="disabled" class="save-button" (click)="savePolicy()" color="primary" type="submit"
|
|
||||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['org.idp.read']">
|
<ng-template appHasRole [appHasRole]="['org.idp.read']">
|
||||||
<h2>{{ 'IDP.LIST.TITLE' | translate }}</h2>
|
<h2>{{ 'IDP.LIST.TITLE' | translate }}</h2>
|
||||||
<p>{{ 'IDP.LIST.DESCRIPTION' | translate }}</p>
|
<p>{{ 'IDP.LIST.DESCRIPTION' | translate }}</p>
|
||||||
@ -73,4 +96,4 @@
|
|||||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false">
|
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false">
|
||||||
</app-idp-table>
|
</app-idp-table>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-detail-layout>
|
</app-detail-layout>
|
||||||
|
@ -37,6 +37,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subdesc {
|
||||||
|
color: var(--grey);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.idps {
|
.idps {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0 -.5rem;
|
margin: 0 -.5rem;
|
||||||
@ -93,3 +98,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--grey);
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
|||||||
|
|
||||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||||
import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component';
|
import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component';
|
||||||
|
import { LoginMethodComponentType } from 'src/app/modules/mfa-table/mfa-table.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login-policy',
|
selector: 'app-login-policy',
|
||||||
@ -29,6 +30,7 @@ import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component
|
|||||||
styleUrls: ['./login-policy.component.scss'],
|
styleUrls: ['./login-policy.component.scss'],
|
||||||
})
|
})
|
||||||
export class LoginPolicyComponent implements OnDestroy {
|
export class LoginPolicyComponent implements OnDestroy {
|
||||||
|
public LoginMethodComponentType: any = LoginMethodComponentType;
|
||||||
public loginData!: LoginPolicyView.AsObject | DefaultLoginPolicyView.AsObject;
|
public loginData!: LoginPolicyView.AsObject | DefaultLoginPolicyView.AsObject;
|
||||||
|
|
||||||
private sub: Subscription = new Subscription();
|
private sub: Subscription = new Subscription();
|
||||||
@ -112,6 +114,8 @@ export class LoginPolicyComponent implements OnDestroy {
|
|||||||
mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
||||||
mgmtreq.setAllowRegister(this.loginData.allowRegister);
|
mgmtreq.setAllowRegister(this.loginData.allowRegister);
|
||||||
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||||
|
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||||
|
// console.log(mgmtreq.toObject());
|
||||||
if ((this.loginData as LoginPolicyView.AsObject).pb_default) {
|
if ((this.loginData as LoginPolicyView.AsObject).pb_default) {
|
||||||
return (this.service as ManagementService).CreateLoginPolicy(mgmtreq);
|
return (this.service as ManagementService).CreateLoginPolicy(mgmtreq);
|
||||||
} else {
|
} else {
|
||||||
@ -122,6 +126,9 @@ export class LoginPolicyComponent implements OnDestroy {
|
|||||||
adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
||||||
adminreq.setAllowRegister(this.loginData.allowRegister);
|
adminreq.setAllowRegister(this.loginData.allowRegister);
|
||||||
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||||
|
adminreq.setForceMfa(this.loginData.forceMfa);
|
||||||
|
// console.log(adminreq.toObject());
|
||||||
|
|
||||||
return (this.service as AdminService).UpdateDefaultLoginPolicy(adminreq);
|
return (this.service as AdminService).UpdateDefaultLoginPolicy(adminreq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
|||||||
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
|
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
|
||||||
import { LoginPolicyRoutingModule } from './login-policy-routing.module';
|
import { LoginPolicyRoutingModule } from './login-policy-routing.module';
|
||||||
import { LoginPolicyComponent } from './login-policy.component';
|
import { LoginPolicyComponent } from './login-policy.component';
|
||||||
|
import { MfaTableModule } from 'src/app/modules/mfa-table/mfa-table.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [LoginPolicyComponent],
|
declarations: [LoginPolicyComponent],
|
||||||
@ -36,6 +37,7 @@ import { LoginPolicyComponent } from './login-policy.component';
|
|||||||
DetailLayoutModule,
|
DetailLayoutModule,
|
||||||
AddIdpDialogModule,
|
AddIdpDialogModule,
|
||||||
IdpTableModule,
|
IdpTableModule,
|
||||||
|
MfaTableModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="getMFAs()" [dataSize]="dataSource?.data?.length">
|
||||||
<table class="table" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="type">
|
<ng-container matColumnDef="type">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="attr">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.ATTRIBUTE' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let mfa"><span *ngIf="mfa?.attr" class="centered">
|
||||||
|
{{ mfa.attr }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="state">
|
<ng-container matColumnDef="state">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||||
@ -20,7 +28,7 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa">
|
<td mat-cell *matCellDef="let mfa">
|
||||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||||
(click)="deleteMFA(mfa.type)">
|
(click)="deleteMFA(mfa.type, mfa.id)">
|
||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
@ -35,6 +43,11 @@
|
|||||||
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
||||||
<mat-icon class="icon" svgIcon="mdi_radar"></mat-icon>{{'USER.MFA.OTP' | translate}}
|
<mat-icon class="icon" svgIcon="mdi_radar"></mat-icon>{{'USER.MFA.OTP' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
<button class="button" *ngIf="otpAvailable" (click)="addU2F()" mat-stroked-button color="primary"
|
||||||
|
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
||||||
|
<i class="las la-fingerprint"></i>
|
||||||
|
{{'USER.MFA.U2F' | translate}}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="loading$ | async">
|
<div class="spinner-container" *ngIf="loading$ | async">
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
.add-row {
|
.add-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: -.5rem;
|
margin: -.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
margin: .5rem;
|
margin: .5rem;
|
||||||
|
@ -4,11 +4,32 @@ import { MatSort } from '@angular/material/sort';
|
|||||||
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||||
import { MfaOtpResponse, MFAState, MfaType, MultiFactor } from 'src/app/proto/generated/auth_pb';
|
import { MfaOtpResponse, MFAState, MfaType, MultiFactor, WebAuthNResponse } from 'src/app/proto/generated/auth_pb';
|
||||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component';
|
import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component';
|
||||||
|
import { DialogU2FComponent } from '../dialog-u2f/dialog-u2f.component';
|
||||||
|
|
||||||
|
export function _base64ToArrayBuffer(base64: string): any {
|
||||||
|
const binaryString = atob(base64);
|
||||||
|
const len = binaryString.length;
|
||||||
|
const bytes = new Uint8Array(len);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
bytes[i] = binaryString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return bytes.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebAuthNOptions {
|
||||||
|
challenge: string;
|
||||||
|
rp: { name: string, id: string; };
|
||||||
|
user: { name: string, id: string, displayName: string; };
|
||||||
|
pubKeyCredParams: any;
|
||||||
|
authenticatorSelection: { userVerification: string; };
|
||||||
|
timeout: number;
|
||||||
|
attestation: string;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-auth-user-mfa',
|
selector: 'app-auth-user-mfa',
|
||||||
@ -16,7 +37,7 @@ import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component';
|
|||||||
styleUrls: ['./auth-user-mfa.component.scss'],
|
styleUrls: ['./auth-user-mfa.component.scss'],
|
||||||
})
|
})
|
||||||
export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||||
public displayedColumns: string[] = ['type', 'state', 'actions'];
|
public displayedColumns: string[] = ['type', 'attr', 'state', 'actions'];
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
|
|
||||||
@ -29,10 +50,12 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public error: string = '';
|
public error: string = '';
|
||||||
public otpAvailable: boolean = false;
|
public otpAvailable: boolean = false;
|
||||||
constructor(private service: GrpcAuthService, private toast: ToastService, private dialog: MatDialog) { }
|
constructor(private service: GrpcAuthService,
|
||||||
|
private toast: ToastService,
|
||||||
|
private dialog: MatDialog) { }
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.getOTP();
|
this.getMFAs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
@ -50,7 +73,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
dialogRef.afterClosed().subscribe((code) => {
|
dialogRef.afterClosed().subscribe((code) => {
|
||||||
if (code) {
|
if (code) {
|
||||||
this.service.VerifyMfaOTP(code).then(() => {
|
this.service.VerifyMfaOTP(code).then(() => {
|
||||||
this.getOTP();
|
this.getMFAs();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -59,7 +82,40 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getOTP(): void {
|
public verifyU2f(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public addU2F(): void {
|
||||||
|
this.service.AddMyMfaU2F().then((u2fresp) => {
|
||||||
|
const webauthn: WebAuthNResponse.AsObject = u2fresp.toObject();
|
||||||
|
const credOptions: CredentialCreationOptions = JSON.parse(atob(webauthn.publicKey as string));
|
||||||
|
|
||||||
|
if (credOptions.publicKey?.challenge) {
|
||||||
|
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
|
||||||
|
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
|
||||||
|
const dialogRef = this.dialog.open(DialogU2FComponent, {
|
||||||
|
width: '400px',
|
||||||
|
data: {
|
||||||
|
credOptions,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(done => {
|
||||||
|
if (done) {
|
||||||
|
this.getMFAs();
|
||||||
|
} else {
|
||||||
|
this.getMFAs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}, error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMFAs(): void {
|
||||||
this.service.GetMyMfas().then(mfas => {
|
this.service.GetMyMfas().then(mfas => {
|
||||||
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
@ -73,13 +129,13 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteMFA(type: MfaType): void {
|
public deleteMFA(type: MfaType, id?: string): void {
|
||||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
confirmKey: 'ACTIONS.DELETE',
|
confirmKey: 'ACTIONS.DELETE',
|
||||||
cancelKey: 'ACTIONS.CANCEL',
|
cancelKey: 'ACTIONS.CANCEL',
|
||||||
titleKey: 'USER.MFA.DIALOG.OTP_DELETE_TITLE',
|
titleKey: 'USER.MFA.DIALOG.MFA_DELETE_TITLE',
|
||||||
descriptionKey: 'USER.MFA.DIALOG.OTP_DELETE_DESCRIPTION',
|
descriptionKey: 'USER.MFA.DIALOG.MFA_DELETE_DESCRIPTION',
|
||||||
},
|
},
|
||||||
width: '400px',
|
width: '400px',
|
||||||
});
|
});
|
||||||
@ -94,7 +150,19 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
this.dataSource.data.splice(index, 1);
|
this.dataSource.data.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.getOTP();
|
this.getMFAs();
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
} else if (type === MfaType.MFATYPE_U2F && id) {
|
||||||
|
this.service.RemoveMyMfaU2F(id).then(() => {
|
||||||
|
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||||
|
|
||||||
|
const index = this.dataSource.data.findIndex(mfa => mfa.type === type);
|
||||||
|
if (index > -1) {
|
||||||
|
this.dataSource.data.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.getMFAs();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
});
|
});
|
||||||
@ -102,4 +170,6 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<h1 mat-dialog-title>{{'USER.MFA.OTP_DIALOG_TITLE' | translate}}</h1>
|
<h1 mat-dialog-title>{{'USER.MFA.OTP_DIALOG_TITLE' | translate}}</h1>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<p translate>{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}</p>
|
<p>{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}</p>
|
||||||
<div class="qrcode-wrapper">
|
<div class="qrcode-wrapper">
|
||||||
<qrcode *ngIf="data" class="qrcode" [qrdata]="data" [width]="150" [errorCorrectionLevel]="'M'"></qrcode>
|
<qrcode *ngIf="data" class="qrcode" [qrdata]="data" [width]="150" [errorCorrectionLevel]="'M'"></qrcode>
|
||||||
</div>
|
</div>
|
||||||
@ -15,4 +15,4 @@
|
|||||||
<button mat-raised-button class="ok-button" color="primary" (click)="closeDialogWithCode()"><span
|
<button mat-raised-button class="ok-button" color="primary" (click)="closeDialogWithCode()"><span
|
||||||
translate>ACTIONS.OK</span>
|
translate>ACTIONS.OK</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
<h1 mat-dialog-title>{{'USER.MFA.U2F_DIALOG_TITLE' | translate}}</h1>
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<p>{{'USER.MFA.U2F_DIALOG_DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<cnsl-form-field class="form-field" label="Name" required="true">
|
||||||
|
<cnsl-label>{{'USER.MFA.U2F_NAME' | translate}}</cnsl-label>
|
||||||
|
<input cnslInput [(ngModel)]="name" required/>
|
||||||
|
</cnsl-form-field>
|
||||||
|
|
||||||
|
<mat-spinner diameter="30" *ngIf="loading"></mat-spinner>
|
||||||
|
|
||||||
|
<p class="error">{{error}}</p>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions class="action">
|
||||||
|
<button mat-button (click)="closeDialog()">{{'ACTIONS.CLOSE' | translate}}</button>
|
||||||
|
<button [disabled]="!name" mat-raised-button class="ok-button" color="primary" (click)="closeDialogWithCode()">{{'ACTIONS.VERIFY' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -0,0 +1,27 @@
|
|||||||
|
.qrcode-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
|
||||||
|
.qrcode {
|
||||||
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #f44336;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: .5rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
export function _arrayBufferToBase64(buffer: any): string {
|
||||||
|
let binary = '';
|
||||||
|
const bytes = new Uint8Array(buffer);
|
||||||
|
const len = bytes.byteLength;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[i]);
|
||||||
|
}
|
||||||
|
return btoa(binary).replace(/\+/g, '-')
|
||||||
|
.replace(/\//g, '_')
|
||||||
|
.replace(/=/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dialog-u2f',
|
||||||
|
templateUrl: './dialog-u2f.component.html',
|
||||||
|
styleUrls: ['./dialog-u2f.component.scss'],
|
||||||
|
})
|
||||||
|
export class DialogU2FComponent {
|
||||||
|
public name: string = '';
|
||||||
|
public error: string = '';
|
||||||
|
public loading: boolean = false;
|
||||||
|
|
||||||
|
constructor(public dialogRef: MatDialogRef<DialogU2FComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: { credOptions: any; },
|
||||||
|
private service: GrpcAuthService, private translate: TranslateService, private toast: ToastService) { }
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialogWithCode(): void {
|
||||||
|
this.error = '';
|
||||||
|
this.loading = true;
|
||||||
|
if (this.name && this.data.credOptions.publicKey) {
|
||||||
|
// this.data.credOptions.publicKey.rp.id = 'localhost';
|
||||||
|
navigator.credentials.create(this.data.credOptions).then((resp) => {
|
||||||
|
if (resp &&
|
||||||
|
(resp as any).response.attestationObject &&
|
||||||
|
(resp as any).response.clientDataJSON &&
|
||||||
|
(resp as any).rawId) {
|
||||||
|
|
||||||
|
const attestationObject = (resp as any).response.attestationObject;
|
||||||
|
const clientDataJSON = (resp as any).response.clientDataJSON;
|
||||||
|
const rawId = (resp as any).rawId;
|
||||||
|
|
||||||
|
const data = JSON.stringify({
|
||||||
|
id: resp.id,
|
||||||
|
rawId: _arrayBufferToBase64(rawId),
|
||||||
|
type: resp.type,
|
||||||
|
response: {
|
||||||
|
attestationObject: _arrayBufferToBase64(attestationObject),
|
||||||
|
clientDataJSON: _arrayBufferToBase64(clientDataJSON),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const base64 = btoa(data);
|
||||||
|
this.service.VerifyMyMfaU2F(base64, this.name).then(() => {
|
||||||
|
this.translate.get('USER.MFA.U2F_SUCCESS').pipe(take(1)).subscribe(msg => {
|
||||||
|
this.toast.showInfo(msg);
|
||||||
|
});
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
this.loading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.loading = false;
|
||||||
|
this.translate.get('USER.MFA.U2F_ERROR').pipe(take(1)).subscribe(msg => {
|
||||||
|
this.toast.showInfo(msg);
|
||||||
|
});
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.error = error;
|
||||||
|
this.toast.showInfo(error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@ import { AuthUserDetailComponent } from './auth-user-detail/auth-user-detail.com
|
|||||||
import { AuthUserMfaComponent } from './auth-user-detail/auth-user-mfa/auth-user-mfa.component';
|
import { AuthUserMfaComponent } from './auth-user-detail/auth-user-mfa/auth-user-mfa.component';
|
||||||
import { CodeDialogComponent } from './auth-user-detail/code-dialog/code-dialog.component';
|
import { CodeDialogComponent } from './auth-user-detail/code-dialog/code-dialog.component';
|
||||||
import { DialogOtpComponent } from './auth-user-detail/dialog-otp/dialog-otp.component';
|
import { DialogOtpComponent } from './auth-user-detail/dialog-otp/dialog-otp.component';
|
||||||
|
import { DialogU2FComponent } from './auth-user-detail/dialog-u2f/dialog-u2f.component';
|
||||||
import { EditDialogComponent } from './auth-user-detail/edit-dialog/edit-dialog.component';
|
import { EditDialogComponent } from './auth-user-detail/edit-dialog/edit-dialog.component';
|
||||||
import { ResendEmailDialogComponent } from './auth-user-detail/resend-email-dialog/resend-email-dialog.component';
|
import { ResendEmailDialogComponent } from './auth-user-detail/resend-email-dialog/resend-email-dialog.component';
|
||||||
import { ThemeSettingComponent } from './auth-user-detail/theme-setting/theme-setting.component';
|
import { ThemeSettingComponent } from './auth-user-detail/theme-setting/theme-setting.component';
|
||||||
@ -65,6 +66,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
|||||||
ExternalIdpsComponent,
|
ExternalIdpsComponent,
|
||||||
ContactComponent,
|
ContactComponent,
|
||||||
ResendEmailDialogComponent,
|
ResendEmailDialogComponent,
|
||||||
|
DialogU2FComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
UserDetailRoutingModule,
|
UserDetailRoutingModule,
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="getMFAs()" [dataSize]="dataSource?.data?.length">
|
||||||
<table class="table" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="type">
|
<ng-container matColumnDef="type">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="attr">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.ATTRIBUTE' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let mfa"><span *ngIf="mfa?.attr" class="centered">
|
||||||
|
{{ mfa.attr }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="state">
|
<ng-container matColumnDef="state">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa">
|
<td mat-cell *matCellDef="let mfa">
|
||||||
@ -21,7 +29,7 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa">
|
<td mat-cell *matCellDef="let mfa">
|
||||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||||
(click)="deleteMFA(mfa.type)">
|
(click)="deleteMFA(mfa.type, mfa.id)">
|
||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
@ -20,7 +20,7 @@ export interface MFAItem {
|
|||||||
styleUrls: ['./user-mfa.component.scss'],
|
styleUrls: ['./user-mfa.component.scss'],
|
||||||
})
|
})
|
||||||
export class UserMfaComponent implements OnInit, OnDestroy {
|
export class UserMfaComponent implements OnInit, OnDestroy {
|
||||||
public displayedColumns: string[] = ['type', 'state', 'actions'];
|
public displayedColumns: string[] = ['type', 'attr', 'state', 'actions'];
|
||||||
@Input() private user!: UserView.AsObject;
|
@Input() private user!: UserView.AsObject;
|
||||||
public mfaSubject: BehaviorSubject<UserMultiFactor.AsObject[]> = new BehaviorSubject<UserMultiFactor.AsObject[]>([]);
|
public mfaSubject: BehaviorSubject<UserMultiFactor.AsObject[]> = new BehaviorSubject<UserMultiFactor.AsObject[]>([]);
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
@ -37,7 +37,7 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
|||||||
constructor(private mgmtUserService: ManagementService, private dialog: MatDialog, private toast: ToastService) { }
|
constructor(private mgmtUserService: ManagementService, private dialog: MatDialog, private toast: ToastService) { }
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.getOTP();
|
this.getMFAs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
@ -45,7 +45,7 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
|||||||
this.loadingSubject.complete();
|
this.loadingSubject.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getOTP(): void {
|
public getMFAs(): void {
|
||||||
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
||||||
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
@ -54,13 +54,14 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteMFA(type: MfaType): void {
|
public deleteMFA(type: MfaType, id?: string): void {
|
||||||
|
console.log(type, id);
|
||||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
confirmKey: 'ACTIONS.DELETE',
|
confirmKey: 'ACTIONS.DELETE',
|
||||||
cancelKey: 'ACTIONS.CANCEL',
|
cancelKey: 'ACTIONS.CANCEL',
|
||||||
titleKey: 'USER.MFA.DIALOG.OTP_DELETE_TITLE',
|
titleKey: 'USER.MFA.DIALOG.MFA_DELETE_TITLE',
|
||||||
descriptionKey: 'USER.MFA.DIALOG.OTP_DELETE_DESCRIPTION',
|
descriptionKey: 'USER.MFA.DIALOG.MFA_DELETE_DESCRIPTION',
|
||||||
},
|
},
|
||||||
width: '400px',
|
width: '400px',
|
||||||
});
|
});
|
||||||
@ -75,7 +76,19 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
|||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
this.dataSource.data.splice(index, 1);
|
this.dataSource.data.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.getOTP();
|
this.getMFAs();
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
} else if (type === MfaType.MFATYPE_U2F && id) {
|
||||||
|
this.mgmtUserService.RemoveMfaU2F(id).then(() => {
|
||||||
|
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||||
|
|
||||||
|
const index = this.dataSource.data.findIndex(mfa => mfa.type === type);
|
||||||
|
if (index > -1) {
|
||||||
|
this.dataSource.data.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.getMFAs();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="userResult?.totalResult"
|
||||||
[timestamp]="userResult?.viewTimestamp" [selection]="selection"
|
[timestamp]="userResult?.viewTimestamp" [selection]="selection"
|
||||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes">
|
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes">
|
||||||
<cnsl-form-field @appearfade *ngIf="userSearchKey != undefined" actions class="filtername">
|
<cnsl-form-field @appearfade *ngIf="userSearchKey != undefined" actions class="filtername">
|
||||||
|
@ -36,6 +36,8 @@ import {
|
|||||||
IdpSearchRequest,
|
IdpSearchRequest,
|
||||||
IdpSearchResponse,
|
IdpSearchResponse,
|
||||||
IdpView,
|
IdpView,
|
||||||
|
MultiFactor,
|
||||||
|
MultiFactorsResult,
|
||||||
OidcIdpConfig,
|
OidcIdpConfig,
|
||||||
OidcIdpConfigCreate,
|
OidcIdpConfigCreate,
|
||||||
OidcIdpConfigUpdate,
|
OidcIdpConfigUpdate,
|
||||||
@ -46,6 +48,8 @@ import {
|
|||||||
OrgSetUpRequest,
|
OrgSetUpRequest,
|
||||||
OrgSetUpResponse,
|
OrgSetUpResponse,
|
||||||
RemoveIamMemberRequest,
|
RemoveIamMemberRequest,
|
||||||
|
SecondFactor,
|
||||||
|
SecondFactorsResult,
|
||||||
ViewID,
|
ViewID,
|
||||||
Views,
|
Views,
|
||||||
} from '../proto/generated/admin_pb';
|
} from '../proto/generated/admin_pb';
|
||||||
@ -73,6 +77,32 @@ export class AdminService {
|
|||||||
return this.grpcService.admin.setUpOrg(req);
|
return this.grpcService.admin.setUpOrg(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDefaultLoginPolicyMultiFactors(): Promise<MultiFactorsResult> {
|
||||||
|
const req = new Empty();
|
||||||
|
return this.grpcService.admin.getDefaultLoginPolicyMultiFactors(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMultiFactorToDefaultLoginPolicy(req: MultiFactor): Promise<MultiFactor> {
|
||||||
|
return this.grpcService.admin.addMultiFactorToDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveMultiFactorFromDefaultLoginPolicy(req: MultiFactor): Promise<Empty> {
|
||||||
|
return this.grpcService.admin.removeMultiFactorFromDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetDefaultLoginPolicySecondFactors(): Promise<SecondFactorsResult> {
|
||||||
|
const req = new Empty();
|
||||||
|
return this.grpcService.admin.getDefaultLoginPolicySecondFactors(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddSecondFactorToDefaultLoginPolicy(req: SecondFactor): Promise<SecondFactor> {
|
||||||
|
return this.grpcService.admin.addSecondFactorToDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveSecondFactorFromDefaultLoginPolicy(req: SecondFactor): Promise<Empty> {
|
||||||
|
return this.grpcService.admin.removeSecondFactorFromDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
public GetIamMemberRoles(): Promise<IamMemberRoles> {
|
public GetIamMemberRoles(): Promise<IamMemberRoles> {
|
||||||
const req = new Empty();
|
const req = new Empty();
|
||||||
return this.grpcService.admin.getIamMemberRoles(req);
|
return this.grpcService.admin.getIamMemberRoles(req);
|
||||||
|
@ -5,34 +5,37 @@ import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs';
|
|||||||
import { catchError, filter, finalize, first, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators';
|
import { catchError, filter, finalize, first, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Changes,
|
Changes,
|
||||||
ChangesRequest,
|
ChangesRequest,
|
||||||
ExternalIDPRemoveRequest,
|
ExternalIDPRemoveRequest,
|
||||||
ExternalIDPSearchRequest,
|
ExternalIDPSearchRequest,
|
||||||
ExternalIDPSearchResponse,
|
ExternalIDPSearchResponse,
|
||||||
Gender,
|
Gender,
|
||||||
MfaOtpResponse,
|
MfaOtpResponse,
|
||||||
MultiFactors,
|
MultiFactors,
|
||||||
MyPermissions,
|
MyPermissions,
|
||||||
MyProjectOrgSearchQuery,
|
MyProjectOrgSearchQuery,
|
||||||
MyProjectOrgSearchRequest,
|
MyProjectOrgSearchRequest,
|
||||||
MyProjectOrgSearchResponse,
|
MyProjectOrgSearchResponse,
|
||||||
Org,
|
Org,
|
||||||
PasswordChange,
|
PasswordChange,
|
||||||
PasswordComplexityPolicy,
|
PasswordComplexityPolicy,
|
||||||
UpdateUserAddressRequest,
|
UpdateUserAddressRequest,
|
||||||
UpdateUserEmailRequest,
|
UpdateUserEmailRequest,
|
||||||
UpdateUserPhoneRequest,
|
UpdateUserPhoneRequest,
|
||||||
UpdateUserProfileRequest,
|
UpdateUserProfileRequest,
|
||||||
UserAddress,
|
UserAddress,
|
||||||
UserEmail,
|
UserEmail,
|
||||||
UserPhone,
|
UserPhone,
|
||||||
UserProfile,
|
UserProfile,
|
||||||
UserProfileView,
|
UserProfileView,
|
||||||
UserSessionViews,
|
UserSessionViews,
|
||||||
UserView,
|
UserView,
|
||||||
VerifyMfaOtp,
|
VerifyMfaOtp,
|
||||||
VerifyUserPhoneRequest,
|
VerifyUserPhoneRequest,
|
||||||
|
VerifyWebAuthN,
|
||||||
|
WebAuthNResponse,
|
||||||
|
WebAuthNTokenID,
|
||||||
} from '../proto/generated/auth_pb';
|
} from '../proto/generated/auth_pb';
|
||||||
import { GrpcService } from './grpc.service';
|
import { GrpcService } from './grpc.service';
|
||||||
import { StorageKey, StorageService } from './storage.service';
|
import { StorageKey, StorageService } from './storage.service';
|
||||||
@ -328,9 +331,31 @@ export class GrpcAuthService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AddMyMfaU2F(): Promise<WebAuthNResponse> {
|
||||||
|
return this.grpcService.auth.addMyMfaU2F(
|
||||||
|
new Empty(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveMyMfaU2F(id: string): Promise<Empty> {
|
||||||
|
const req = new WebAuthNTokenID();
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.auth.removeMyMfaU2F(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerifyMyMfaU2F(credential: string, tokenname: string): Promise<Empty> {
|
||||||
|
const req = new VerifyWebAuthN();
|
||||||
|
req.setPublicKeyCredential(credential);
|
||||||
|
req.setTokenName(tokenname);
|
||||||
|
|
||||||
|
return this.grpcService.auth.verifyMyMfaU2F(
|
||||||
|
req,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public RemoveMfaOTP(): Promise<Empty> {
|
public RemoveMfaOTP(): Promise<Empty> {
|
||||||
return this.grpcService.auth.removeMfaOTP(
|
return this.grpcService.auth.removeMfaOTP(
|
||||||
new Empty(),
|
new Empty(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
|
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
|
||||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { MultiFactorsResult } from '../proto/generated/admin_pb';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AddMachineKeyRequest,
|
AddMachineKeyRequest,
|
||||||
@ -50,6 +51,7 @@ import {
|
|||||||
MachineKeySearchResponse,
|
MachineKeySearchResponse,
|
||||||
MachineKeyType,
|
MachineKeyType,
|
||||||
MachineResponse,
|
MachineResponse,
|
||||||
|
MultiFactor,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
OIDCApplicationCreate,
|
OIDCApplicationCreate,
|
||||||
OIDCConfig,
|
OIDCConfig,
|
||||||
@ -122,6 +124,8 @@ import {
|
|||||||
ProjectView,
|
ProjectView,
|
||||||
RemoveOrgDomainRequest,
|
RemoveOrgDomainRequest,
|
||||||
RemoveOrgMemberRequest,
|
RemoveOrgMemberRequest,
|
||||||
|
SecondFactor,
|
||||||
|
SecondFactorsResult,
|
||||||
SetPasswordNotificationRequest,
|
SetPasswordNotificationRequest,
|
||||||
UpdateMachineRequest,
|
UpdateMachineRequest,
|
||||||
UpdateUserAddressRequest,
|
UpdateUserAddressRequest,
|
||||||
@ -152,6 +156,7 @@ import {
|
|||||||
UserSearchResponse,
|
UserSearchResponse,
|
||||||
UserView,
|
UserView,
|
||||||
ValidateOrgDomainRequest,
|
ValidateOrgDomainRequest,
|
||||||
|
WebAuthNTokenID,
|
||||||
ZitadelDocs,
|
ZitadelDocs,
|
||||||
} from '../proto/generated/management_pb';
|
} from '../proto/generated/management_pb';
|
||||||
import { GrpcService } from './grpc.service';
|
import { GrpcService } from './grpc.service';
|
||||||
@ -185,6 +190,32 @@ export class ManagementService {
|
|||||||
return this.grpcService.mgmt.searchIdps(req);
|
return this.grpcService.mgmt.searchIdps(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GetLoginPolicyMultiFactors(): Promise<MultiFactorsResult> {
|
||||||
|
const req = new Empty();
|
||||||
|
return this.grpcService.mgmt.getLoginPolicyMultiFactors(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddMultiFactorToLoginPolicy(req: MultiFactor): Promise<MultiFactor> {
|
||||||
|
return this.grpcService.mgmt.addMultiFactorToLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveMultiFactorFromLoginPolicy(req: MultiFactor): Promise<Empty> {
|
||||||
|
return this.grpcService.mgmt.removeMultiFactorFromLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetLoginPolicySecondFactors(): Promise<SecondFactorsResult> {
|
||||||
|
const req = new Empty();
|
||||||
|
return this.grpcService.mgmt.getLoginPolicySecondFactors(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddSecondFactorToLoginPolicy(req: SecondFactor): Promise<SecondFactor> {
|
||||||
|
return this.grpcService.mgmt.addSecondFactorToLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoveSecondFactorFromLoginPolicy(req: SecondFactor): Promise<Empty> {
|
||||||
|
return this.grpcService.mgmt.removeSecondFactorFromLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
public GetLoginPolicy(): Promise<LoginPolicyView> {
|
public GetLoginPolicy(): Promise<LoginPolicyView> {
|
||||||
const req = new Empty();
|
const req = new Empty();
|
||||||
return this.grpcService.mgmt.getLoginPolicy(req);
|
return this.grpcService.mgmt.getLoginPolicy(req);
|
||||||
@ -683,6 +714,12 @@ export class ManagementService {
|
|||||||
return this.grpcService.mgmt.removeMfaOTP(req);
|
return this.grpcService.mgmt.removeMfaOTP(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RemoveMfaU2F(id: string): Promise<Empty> {
|
||||||
|
const req = new WebAuthNTokenID();
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.mgmt.removeMfaU2F(req);
|
||||||
|
}
|
||||||
|
|
||||||
public SaveUserProfile(
|
public SaveUserProfile(
|
||||||
id: string,
|
id: string,
|
||||||
firstName?: string,
|
firstName?: string,
|
||||||
|
@ -155,6 +155,7 @@
|
|||||||
"MFA": {
|
"MFA": {
|
||||||
"TABLETYPE":"Typ",
|
"TABLETYPE":"Typ",
|
||||||
"TABLESTATE":"Status",
|
"TABLESTATE":"Status",
|
||||||
|
"ATTRIBUTE":"Attribut",
|
||||||
"TABLEACTIONS":"Aktionen",
|
"TABLEACTIONS":"Aktionen",
|
||||||
"TITLE": "Multifaktor-Authentisierung",
|
"TITLE": "Multifaktor-Authentisierung",
|
||||||
"DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um Dein Konto optimal zu schützen.",
|
"DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um Dein Konto optimal zu schützen.",
|
||||||
@ -162,6 +163,12 @@
|
|||||||
"OTP": "OTP konfigurieren",
|
"OTP": "OTP konfigurieren",
|
||||||
"OTP_DIALOG_TITLE": "OTP hinzufügen",
|
"OTP_DIALOG_TITLE": "OTP hinzufügen",
|
||||||
"OTP_DIALOG_DESCRIPTION": "Scanne den QR-Code mit einer Authenticator App und verifiziere den erhaltenen Code, um OTP zu aktivieren.",
|
"OTP_DIALOG_DESCRIPTION": "Scanne den QR-Code mit einer Authenticator App und verifiziere den erhaltenen Code, um OTP zu aktivieren.",
|
||||||
|
"U2F":"U2F hinzufügen",
|
||||||
|
"U2F_DIALOG_TITLE": "U2F hinzufügen",
|
||||||
|
"U2F_DIALOG_DESCRIPTION": "Gib einen Namen für den von dir verwendeten Universellen Multifaktor an.",
|
||||||
|
"U2F_SUCCESS":"U2F erfolgreich erstellt!",
|
||||||
|
"U2F_ERROR":"Ein Fehler ist aufgetreten!",
|
||||||
|
"U2F_NAME":"U2F Name",
|
||||||
"TYPE": {
|
"TYPE": {
|
||||||
"0":"Keine MFA definiert",
|
"0":"Keine MFA definiert",
|
||||||
"1":"OTP",
|
"1":"OTP",
|
||||||
@ -174,8 +181,8 @@
|
|||||||
"3": "Gelöscht"
|
"3": "Gelöscht"
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
"OTP_DELETE_TITLE":"OTP Faktor entfernen",
|
"MFA_DELETE_TITLE":"Zweiten Faktor entfernen",
|
||||||
"OTP_DELETE_DESCRIPTION":"Sie sind im Begriff OTP als Zweitfaktormethode zu entfernen. Sind sie sicher?"
|
"MFA_DELETE_DESCRIPTION":"Sie sind im Begriff eine Zweitfaktormethode zu entfernen. Sind sie sicher?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EXTERNALIDP": {
|
"EXTERNALIDP": {
|
||||||
@ -345,6 +352,7 @@
|
|||||||
"PHONEVERIFICATIONSENT":"Bestätigungscode per Telefonnummer gesendet.",
|
"PHONEVERIFICATIONSENT":"Bestätigungscode per Telefonnummer gesendet.",
|
||||||
"EMAILVERIFICATIONSENT":"Bestätigungscode per E-Mail gesendet.",
|
"EMAILVERIFICATIONSENT":"Bestätigungscode per E-Mail gesendet.",
|
||||||
"OTPREMOVED":"OTP entfernt.",
|
"OTPREMOVED":"OTP entfernt.",
|
||||||
|
"U2FREMOVED":"U2F entfernt.",
|
||||||
"INITIALPASSWORDSET":"Initiales Passwort gesetzt.",
|
"INITIALPASSWORDSET":"Initiales Passwort gesetzt.",
|
||||||
"PASSWORDNOTIFICATIONSENT":"Passwortänderung mittgeteilt.",
|
"PASSWORDNOTIFICATIONSENT":"Passwortänderung mittgeteilt.",
|
||||||
"PASSWORDCHANGED":"Passwort geändert.",
|
"PASSWORDCHANGED":"Passwort geändert.",
|
||||||
@ -551,7 +559,9 @@
|
|||||||
"ALLOWREGISTER":"Registrieren erlaubt",
|
"ALLOWREGISTER":"Registrieren erlaubt",
|
||||||
"ALLOWUSERNAMEPASSWORD_DESC":"Der konventionelle Login mit Benutzername und Passwort wird erlaubt.",
|
"ALLOWUSERNAMEPASSWORD_DESC":"Der konventionelle Login mit Benutzername und Passwort wird erlaubt.",
|
||||||
"ALLOWEXTERNALIDP_DESC":"Der Login wird für die darunter liegenden Identity Provider erlaubt.",
|
"ALLOWEXTERNALIDP_DESC":"Der Login wird für die darunter liegenden Identity Provider erlaubt.",
|
||||||
"ALLOWREGISTER_DESC":"Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers."
|
"ALLOWREGISTER_DESC":"Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.",
|
||||||
|
"FORCEMFA":"Mfa erzwingen",
|
||||||
|
"FORCEMFA_DESC":"Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden."
|
||||||
},
|
},
|
||||||
"RESET":"Richtlinie zurücksetzen",
|
"RESET":"Richtlinie zurücksetzen",
|
||||||
"CREATECUSTOM":"Benutzerdefinierte Richtlinie erstellen",
|
"CREATECUSTOM":"Benutzerdefinierte Richtlinie erstellen",
|
||||||
@ -763,7 +773,7 @@
|
|||||||
},
|
},
|
||||||
"IDP":{
|
"IDP":{
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"TITLE":"Identitäts Providers",
|
"TITLE":"Identitäts Provider",
|
||||||
"DESCRIPTION":"Definieren Sie hier Ihre zusätzlichen Idps, die sie für die Authentifizierung in Ihren Organisationen verwenden können."
|
"DESCRIPTION":"Definieren Sie hier Ihre zusätzlichen Idps, die sie für die Authentifizierung in Ihren Organisationen verwenden können."
|
||||||
},
|
},
|
||||||
"CREATE": {
|
"CREATE": {
|
||||||
@ -826,6 +836,37 @@
|
|||||||
"DELETED":"Idp erfolgreich gelöscht!"
|
"DELETED":"Idp erfolgreich gelöscht!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"MFA":{
|
||||||
|
"LIST": {
|
||||||
|
"MULTIFACTORTITLE":"Multifaktoren",
|
||||||
|
"MULTIFACTORDESCRIPTION":"Definieren Sie hier Ihre Multifaktoren, die sie für die Authentifizierung verwenden können.",
|
||||||
|
"SECONDFACTORTITLE":"Zweitfaktoren",
|
||||||
|
"SECONDFACTORDESCRIPTION":"Definieren Sie hier Ihre Zweitfaktoren, die sie für die Authentifizierung verwenden können."
|
||||||
|
},
|
||||||
|
"CREATE": {
|
||||||
|
"TITLE":"Neuer Faktor",
|
||||||
|
"DESCRIPTION":"Definieren Sie hier den gewünschten Typ."
|
||||||
|
},
|
||||||
|
"DELETE": {
|
||||||
|
"TITLE":"Faktor löschen",
|
||||||
|
"DESCRIPTION":"Sie sind im Begriff einen Faktor zu löschen. Die daruch hervorgerufenen Änderungen sind unwiederruflich. Wollen Sie dies wirklich tun?"
|
||||||
|
},
|
||||||
|
"TOAST": {
|
||||||
|
"ADDED":"Erfolgreich hinzugefügt.",
|
||||||
|
"SAVED": "Erfolgreich gespeichert.",
|
||||||
|
"DELETED":"Mfa erfolgreich gelöscht!"
|
||||||
|
},
|
||||||
|
"TYPE":"Typ",
|
||||||
|
"MULTIFACTORTYPES": {
|
||||||
|
"0":"Unknown",
|
||||||
|
"1":"U2F with Pin"
|
||||||
|
},
|
||||||
|
"SECONDFACTORTYPES": {
|
||||||
|
"0":"Unknown",
|
||||||
|
"1":"OTP",
|
||||||
|
"2":"U2F"
|
||||||
|
}
|
||||||
|
},
|
||||||
"LOGINPOLICY": {
|
"LOGINPOLICY": {
|
||||||
"CREATE": {
|
"CREATE": {
|
||||||
"TITLE":"Login Policy",
|
"TITLE":"Login Policy",
|
||||||
|
@ -155,6 +155,7 @@
|
|||||||
"MFA": {
|
"MFA": {
|
||||||
"TABLETYPE":"Type",
|
"TABLETYPE":"Type",
|
||||||
"TABLESTATE":"Status",
|
"TABLESTATE":"Status",
|
||||||
|
"ATTRIBUTE":"Attribut",
|
||||||
"TABLEACTIONS":"Actions",
|
"TABLEACTIONS":"Actions",
|
||||||
"TITLE": "Multifactor Authentication",
|
"TITLE": "Multifactor Authentication",
|
||||||
"DESCRIPTION": "Add a second factor to ensure optimal security for your account.",
|
"DESCRIPTION": "Add a second factor to ensure optimal security for your account.",
|
||||||
@ -162,6 +163,12 @@
|
|||||||
"OTP": "Configure OTP",
|
"OTP": "Configure OTP",
|
||||||
"OTP_DIALOG_TITLE": "Add OTP",
|
"OTP_DIALOG_TITLE": "Add OTP",
|
||||||
"OTP_DIALOG_DESCRIPTION": "Scan the QR code with an authenticator app and enter the code below to verify and activate the OTP method.",
|
"OTP_DIALOG_DESCRIPTION": "Scan the QR code with an authenticator app and enter the code below to verify and activate the OTP method.",
|
||||||
|
"U2F":"Add U2F",
|
||||||
|
"U2F_DIALOG_TITLE": "Verify U2F",
|
||||||
|
"U2F_DIALOG_DESCRIPTION": "Enter a name for your used universal Multifactor.",
|
||||||
|
"U2F_SUCCESS":"U2F created successfully!",
|
||||||
|
"U2F_ERROR":"An error during U2F setup occurred!",
|
||||||
|
"U2F_NAME":"U2F Name",
|
||||||
"TYPE": {
|
"TYPE": {
|
||||||
"0": "No MFA defined",
|
"0": "No MFA defined",
|
||||||
"1": "OTP",
|
"1": "OTP",
|
||||||
@ -174,8 +181,8 @@
|
|||||||
"3": "Deleted"
|
"3": "Deleted"
|
||||||
},
|
},
|
||||||
"DIALOG": {
|
"DIALOG": {
|
||||||
"OTP_DELETE_TITLE":"Remove OTP Factor",
|
"MFA_DELETE_TITLE":"Remove Secondfactor",
|
||||||
"OTP_DELETE_DESCRIPTION":"You are about to delete OTP as second factor. Are you sure?"
|
"MFA_DELETE_DESCRIPTION":"You are about to delete a second factor. Are you sure?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EXTERNALIDP": {
|
"EXTERNALIDP": {
|
||||||
@ -345,6 +352,7 @@
|
|||||||
"PHONEVERIFICATIONSENT":"Phone verification code sent.",
|
"PHONEVERIFICATIONSENT":"Phone verification code sent.",
|
||||||
"EMAILVERIFICATIONSENT":"E-mail verification code sent.",
|
"EMAILVERIFICATIONSENT":"E-mail verification code sent.",
|
||||||
"OTPREMOVED":"OTP removed.",
|
"OTPREMOVED":"OTP removed.",
|
||||||
|
"U2FREMOVED":"U2F removed.",
|
||||||
"INITIALPASSWORDSET":"Initial password set.",
|
"INITIALPASSWORDSET":"Initial password set.",
|
||||||
"PASSWORDNOTIFICATIONSENT":"Password change notification sent.",
|
"PASSWORDNOTIFICATIONSENT":"Password change notification sent.",
|
||||||
"PASSWORDCHANGED":"Password changed successfully.",
|
"PASSWORDCHANGED":"Password changed successfully.",
|
||||||
@ -551,7 +559,9 @@
|
|||||||
"ALLOWREGISTER":"Register allowed",
|
"ALLOWREGISTER":"Register allowed",
|
||||||
"ALLOWUSERNAMEPASSWORD_DESC":"The conventional login with user name and password is allowed.",
|
"ALLOWUSERNAMEPASSWORD_DESC":"The conventional login with user name and password is allowed.",
|
||||||
"ALLOWEXTERNALIDP_DESC":"The login is allowed for the underlying identity providers",
|
"ALLOWEXTERNALIDP_DESC":"The login is allowed for the underlying identity providers",
|
||||||
"ALLOWREGISTER_DESC":"If the option is selected, an additional step for registering a user appears in the login."
|
"ALLOWREGISTER_DESC":"If the option is selected, an additional step for registering a user appears in the login.",
|
||||||
|
"FORCEMFA":"Force MFA",
|
||||||
|
"FORCEMFA_DESC":"If the option is selected, users have to configure a second factor for login."
|
||||||
},
|
},
|
||||||
"RESET":"Reset Policy",
|
"RESET":"Reset Policy",
|
||||||
"CREATECUSTOM":"Create Custom Policy",
|
"CREATECUSTOM":"Create Custom Policy",
|
||||||
@ -826,6 +836,37 @@
|
|||||||
"DELETED":"Idp removed successfully!"
|
"DELETED":"Idp removed successfully!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"MFA":{
|
||||||
|
"LIST": {
|
||||||
|
"MULTIFACTORTITLE":"Multifactors",
|
||||||
|
"MULTIFACTORDESCRIPTION":"Define your Multifactors for Authentication here.",
|
||||||
|
"SECONDFACTORTITLE":"Secondfactors",
|
||||||
|
"SECONDFACTORDESCRIPTION":"Define your Secondfactors for Authentication here."
|
||||||
|
},
|
||||||
|
"CREATE": {
|
||||||
|
"TITLE":"New Factor",
|
||||||
|
"DESCRIPTION":"Select your new Factor type."
|
||||||
|
},
|
||||||
|
"DELETE": {
|
||||||
|
"TITLE":"Delete Factor",
|
||||||
|
"DESCRIPTION":"You are about to delete a Factor from Login Policy. Are you sure?"
|
||||||
|
},
|
||||||
|
"TOAST": {
|
||||||
|
"ADDED":"Added successfully.",
|
||||||
|
"SAVED": "Saved successfully.",
|
||||||
|
"DELETED":"Removed successfully"
|
||||||
|
},
|
||||||
|
"TYPE":"Type",
|
||||||
|
"MULTIFACTORTYPES": {
|
||||||
|
"0":"Unknown",
|
||||||
|
"1":"U2F with Pin"
|
||||||
|
},
|
||||||
|
"SECONDFACTORTYPES": {
|
||||||
|
"0":"Unknown",
|
||||||
|
"1":"OTP",
|
||||||
|
"2":"U2F"
|
||||||
|
}
|
||||||
|
},
|
||||||
"LOGINPOLICY": {
|
"LOGINPOLICY": {
|
||||||
"CREATE": {
|
"CREATE": {
|
||||||
"TITLE":"Login Policy",
|
"TITLE":"Login Policy",
|
||||||
|
@ -226,6 +226,11 @@ func (s *Server) RemoveMfaOTP(ctx context.Context, userID *management.UserID) (*
|
|||||||
return &empty.Empty{}, err
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) RemoveMfaU2F(ctx context.Context, webAuthNTokenID *management.WebAuthNTokenID) (*empty.Empty, error) {
|
||||||
|
err := s.user.RemoveU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id)
|
||||||
|
return &empty.Empty{}, err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) {
|
func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) {
|
||||||
request := userMembershipSearchRequestsToModel(in)
|
request := userMembershipSearchRequestsToModel(in)
|
||||||
request.AppendUserIDQuery(in.UserId)
|
request.AppendUserIDQuery(in.UserId)
|
||||||
|
@ -2,14 +2,15 @@ package management
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
"github.com/caos/zitadel/internal/model"
|
|
||||||
"github.com/golang/protobuf/ptypes"
|
"github.com/golang/protobuf/ptypes"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/eventstore/models"
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
"github.com/caos/zitadel/internal/model"
|
||||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||||
"github.com/caos/zitadel/pkg/grpc/management"
|
"github.com/caos/zitadel/pkg/grpc/management"
|
||||||
"github.com/caos/zitadel/pkg/grpc/message"
|
"github.com/caos/zitadel/pkg/grpc/message"
|
||||||
@ -504,6 +505,7 @@ func mfaFromModel(mfa *usr_model.MultiFactor) *management.UserMultiFactor {
|
|||||||
State: mfaStateFromModel(mfa.State),
|
State: mfaStateFromModel(mfa.State),
|
||||||
Type: mfaTypeFromModel(mfa.Type),
|
Type: mfaTypeFromModel(mfa.Type),
|
||||||
Attribute: mfa.Attribute,
|
Attribute: mfa.Attribute,
|
||||||
|
Id: mfa.ID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +231,10 @@ func (repo *UserRepo) RemoveOTP(ctx context.Context, userID string) error {
|
|||||||
return repo.UserEvents.RemoveOTP(ctx, userID)
|
return repo.UserEvents.RemoveOTP(ctx, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error {
|
||||||
|
return repo.UserEvents.RemoveU2FToken(ctx, userID, webAuthNTokenID)
|
||||||
|
}
|
||||||
|
|
||||||
func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
||||||
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||||
if err != nil && caos_errs.IsNotFound(err) {
|
if err != nil && caos_errs.IsNotFound(err) {
|
||||||
|
@ -32,6 +32,7 @@ type UserRepository interface {
|
|||||||
|
|
||||||
UserMFAs(ctx context.Context, userID string) ([]*model.MultiFactor, error)
|
UserMFAs(ctx context.Context, userID string) ([]*model.MultiFactor, error)
|
||||||
RemoveOTP(ctx context.Context, userID string) error
|
RemoveOTP(ctx context.Context, userID string) error
|
||||||
|
RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error
|
||||||
|
|
||||||
SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error)
|
SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error)
|
||||||
RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error
|
RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error
|
||||||
|
@ -399,6 +399,16 @@ service ManagementService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc RemoveMfaU2F(WebAuthNTokenID) returns (google.protobuf.Empty) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
delete: "/users/{user_id}/mfas/u2f/{id}"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
|
permission: "user.write"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Sends an Notification (Email/SMS) with a password reset Link
|
// Sends an Notification (Email/SMS) with a password reset Link
|
||||||
rpc SendSetPasswordNotification(SetPasswordNotificationRequest) returns (google.protobuf.Empty) {
|
rpc SendSetPasswordNotification(SetPasswordNotificationRequest) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
@ -1646,6 +1656,11 @@ message UserID {
|
|||||||
string id = 1 [(validate.rules).string.min_len = 1];
|
string id = 1 [(validate.rules).string.min_len = 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WebAuthNTokenID {
|
||||||
|
string user_id = 1 [(validate.rules).string.min_len = 1];
|
||||||
|
string id = 2 [(validate.rules).string.min_len = 1];
|
||||||
|
}
|
||||||
|
|
||||||
message LoginName {
|
message LoginName {
|
||||||
string login_name = 1 [(validate.rules).string.min_len = 1];
|
string login_name = 1 [(validate.rules).string.min_len = 1];
|
||||||
}
|
}
|
||||||
@ -2030,6 +2045,7 @@ message UserMultiFactor {
|
|||||||
MfaType type = 1;
|
MfaType type = 1;
|
||||||
MFAState state = 2;
|
MFAState state = 2;
|
||||||
string attribute = 3;
|
string attribute = 3;
|
||||||
|
string id = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MfaType {
|
enum MfaType {
|
||||||
|
Loading…
Reference in New Issue
Block a user