fix: console for neweventstore (#1457)

* some issues

* passwordless, mfa

* mfa, project fixes, login policy

* user table, auth service, interceptor

* keys, machine edit, grants, passwordless

* remove asdf
This commit is contained in:
Max Peintner 2021-03-24 10:50:15 +01:00 committed by GitHub
parent bacfc3b099
commit 08b066b3a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 142 additions and 85 deletions

View File

@ -21,10 +21,13 @@
}
@mixin changes-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
.scroll-container {
max-height: 50vh;
overflow-y: scroll;
border-bottom: 1px solid #ffffff20;
border-bottom: 1px solid if($is-dark-theme, #303131, #e3e8ee);
/* stylelint-enable */
margin-bottom: 0.5rem;
.date {

View File

@ -1,12 +1,6 @@
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
[timestamp]="keyResult?.details?.viewTimestamp" [selection]="selection">
<div actions>
<button color="warn"
[disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) == false"
(click)="deleteSelectedKeys()" matTooltip="{{'ACTIONS.DELETE' | translate}}" class="icon-button"
mat-icon-button *ngIf="selection.hasValue()">
<i class="las la-trash"></i>
</button>
<a [disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) == false"
color="primary" mat-raised-button (click)="openAddKey()">
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
@ -42,7 +36,7 @@
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.CREATIONDATE' | translate }} </th>
<td mat-cell *matCellDef="let key">
{{key.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
{{key.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
</td>
</ng-container>
@ -53,6 +47,17 @@
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let key">
<button
[disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) == false"
mat-icon-button color="warn" matTooltip="{{'ACTIONS.DELETE' | translate}}"
(click)="deleteKey(key)">
<i class="las la-trash"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let key; columns: displayedColumns;"

View File

@ -24,4 +24,14 @@
tr {
outline: none;
button {
visibility: hidden;
}
&:hover {
button {
visibility: visible;
}
}
}

View File

@ -29,7 +29,7 @@ export class ClientKeysComponent implements OnInit {
public keyResult!: ListAppKeysResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate'];
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions'];
@Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter();
@ -62,11 +62,8 @@ export class ClientKeysComponent implements OnInit {
this.getData(event.pageSize, event.pageIndex * event.pageSize);
}
public deleteSelectedKeys(): void {
const mappedDeletions = this.selection.selected.map(value => {
return this.mgmtService.removeAppKey(this.projectId, this.appId, value.id);
});
Promise.all(mappedDeletions).then(() => {
public deleteKey(key: Key.AsObject): void {
this.mgmtService.removeAppKey(this.projectId, this.appId, key.id).then(() => {
this.selection.clear();
this.toast.showInfo('USER.TOAST.SELECTEDKEYSDELETED', true);
this.getData(10, 0);

View File

@ -1,6 +1,7 @@
import { animate, animateChild, keyframes, query, stagger, style, transition, trigger } from '@angular/animations';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
@Component({
selector: 'app-contributors',
@ -29,7 +30,7 @@ export class ContributorsComponent {
@Input() disabled: boolean = false;
@Input() totalResult: number = 0;
@Input() loading: boolean = false;
@Input() membersSubject!: BehaviorSubject<any[]>;
@Input() membersSubject!: BehaviorSubject<Member.AsObject[]>;
@Output() addClicked: EventEmitter<void> = new EventEmitter();
@Output() showDetailClicked: EventEmitter<void> = new EventEmitter();
@Output() refreshClicked: EventEmitter<void> = new EventEmitter();

View File

@ -69,19 +69,19 @@
<td [routerLink]="routerLinkForRow(idp)" class="pointer" mat-cell *matCellDef="let idp">
<div class="date-block">
<span class="date-sub">{{ 'IDP.CREATIONDATE' | translate }}:</span>
<span>{{idp.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
<span>{{idp.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</div>
<div class="date-block">
<span class="date-sub">{{ 'IDP.CHANGEDATE' | translate }}</span>
<span>{{idp.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
<span>{{idp.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</div>
</td>
</ng-container>
<ng-container matColumnDef="type">
<ng-container matColumnDef="owner">
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.TYPE' | translate }} </th>
<td [routerLink]="routerLinkForRow(idp)" class="pointer" mat-cell *matCellDef="let idp">
{{'IDP.TYPES.'+idp.providerType | translate }} </td>
{{'IDP.OWNERTYPES.'+idp.owner | translate }} </td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
@ -89,7 +89,7 @@
<td mat-cell *matCellDef="let idp">
<button
[disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.providerType == IDPOwnerType.IDP_OWNER_TYPE_ORG"
mat-icon-button color="warn" matTooltip="{{'IAM.VIEWS.CLEAR' | translate}}"
mat-icon-button color="warn" matTooltip="{{'IAM.VIEWS.DELETE' | translate}}"
(click)="removeIdp(idp)">
<i class="las la-trash"></i>
</button>

View File

@ -51,7 +51,7 @@ export class IdpTableComponent implements OnInit {
ngOnInit(): void {
this.getData(10, 0);
if (this.serviceType === PolicyComponentServiceType.MGMT) {
this.displayedColumns = ['select', 'name', 'config', 'dates', 'state', 'type'];
this.displayedColumns = ['select', 'name', 'config', 'dates', 'state', 'owner'];
}
if (!this.disabled) {

View File

@ -1,11 +1,6 @@
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
[timestamp]="keyResult?.details?.viewTimestamp" [selection]="selection">
<div actions>
<button color="warn" [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) == false"
(click)="deleteSelectedKeys()" matTooltip="{{'ACTIONS.DELETE' | translate}}" class="icon-button"
mat-icon-button *ngIf="selection.hasValue()">
<i class="las la-trash"></i>
</button>
<a [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) == false" color="primary"
mat-raised-button (click)="openAddKey()">
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
@ -52,6 +47,17 @@
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let key">
<button [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) == false"
mat-icon-button color="warn" matTooltip="{{'ACTIONS.DELETE' | translate}}"
(click)="deleteKey(key)">
<i class="las la-trash"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;">

View File

@ -24,4 +24,14 @@
tr {
outline: none;
button {
visibility: hidden;
}
&:hover {
button {
visibility: visible;
}
}
}

View File

@ -28,7 +28,7 @@ export class MachineKeysComponent implements OnInit {
public keyResult!: ListMachineKeysResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate'];
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions'];
@Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter();
@ -61,11 +61,8 @@ export class MachineKeysComponent implements OnInit {
this.getData(event.pageSize, event.pageIndex * event.pageSize);
}
public deleteSelectedKeys(): void {
const mappedDeletions = this.selection.selected.map(value => {
return this.mgmtService.removeMachineKey(value.id, this.userId);
});
Promise.all(mappedDeletions).then(() => {
public deleteKey(key: Key.AsObject): void {
this.mgmtService.removeMachineKey(key.id, this.userId).then(() => {
this.selection.clear();
this.toast.showInfo('USER.TOAST.SELECTEDKEYSDELETED', true);
this.getData(10, 0);

View File

@ -51,10 +51,10 @@
{{member.lastName}} </td>
</ng-container>
<ng-container matColumnDef="username">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.MEMBER.USERNAME' | translate }} </th>
<ng-container matColumnDef="loginname">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.MEMBER.LOGINNAME' | translate }} </th>
<td class="pointer" [routerLink]="['/users', member.userId]" mat-cell *matCellDef="let member">
{{member.userName}} </td>
{{member.preferredloginname}} </td>
</ng-container>
<ng-container matColumnDef="email">

View File

@ -23,20 +23,20 @@ export class MembersTableComponent implements OnInit, OnDestroy {
@Input() public canDelete: boolean = false;
@Input() public canWrite: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<Member>;
@ViewChild(MatTable) public table!: MatTable<Member.AsObject>;
@Input() public dataSource!: MemberDatasource;
public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
@Input() public memberRoleOptions: string[] = [];
@Input() public factoryLoadFunc!: Function;
@Input() public refreshTrigger!: Observable<void>;
@Output() public updateRoles: EventEmitter<{ member: Member, change: MatSelectChange; }> = new EventEmitter();
@Output() public updateRoles: EventEmitter<{ member: Member.AsObject, change: MatSelectChange; }> = new EventEmitter();
@Output() public changedSelection: EventEmitter<any[]> = new EventEmitter();
@Output() public deleteMember: EventEmitter<Member> = new EventEmitter();
@Output() public deleteMember: EventEmitter<Member.AsObject> = new EventEmitter();
private destroyed: Subject<void> = new Subject();
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'userId', 'firstname', 'lastname', 'username', 'email', 'roles'];
public displayedColumns: string[] = ['select', 'userId', 'firstname', 'lastname', 'loginname', 'email', 'roles'];
constructor() {
this.selection.changed.pipe(takeUntil(this.destroyed)).subscribe(_ => {

View File

@ -214,6 +214,8 @@ export class MfaTableComponent implements OnInit {
});
}
}
console.log(this.mfas);
}
public refreshPageAfterTimout(to: number): void {

View File

@ -49,19 +49,24 @@ export class AddIdpDialogComponent {
public loadIdps(): void {
this.idp = undefined;
if (this.serviceType === PolicyComponentServiceType.MGMT) {
const query: IDPQuery = new IDPQuery();
const idpOTQ: IDPOwnerTypeQuery = new IDPOwnerTypeQuery();
idpOTQ.setOwnerType(this.idpType);
query.setOwnerTypeQuery(idpOTQ);
switch (this.idpType) {
case IDPOwnerType.IDP_OWNER_TYPE_ORG:
const query: IDPQuery = new IDPQuery();
const idpOTQ: IDPOwnerTypeQuery = new IDPOwnerTypeQuery();
idpOTQ.setOwnerType(this.idpType);
query.setOwnerTypeQuery(idpOTQ);
this.mgmtService.listOrgIDPs(undefined, undefined, [query]).then(resp => {
this.availableIdps = resp.resultList;
});
break;
case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM:
this.adminService.listIDPs().then(resp => {
this.availableIdps = resp.resultList;
console.log(resp);
});
break;
this.mgmtService.listOrgIDPs(undefined, undefined, [query]).then(resp => {
this.availableIdps = resp.resultList;
});
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
this.adminService.listIDPs().then(resp => {
this.availableIdps = resp.resultList;
});
}
}

View File

@ -46,7 +46,7 @@
<div class="meta-row">
<span class="first">{{'ORG.PAGES.STATE' | translate}}</span>
<span *ngIf="org && org.state !== undefined" class="state"
[ngClass]="{'active': org.state === OrgState.ORGSTATE_ACTIVE, 'inactive': org.state === OrgState.ORGSTATE_INACTIVE}">{{'ORG.STATE.'+org.state
[ngClass]="{'active': org.state === OrgState.ORG_STATE_ACTIVE, 'inactive': org.state === OrgState.ORG_STATE_INACTIVE}">{{'ORG.STATE.'+org.state
|
translate}}</span>
</div>

View File

@ -141,6 +141,13 @@
<app-client-keys [projectId]="projectId" [appId]="app.id"></app-client-keys>
</app-card>
<div *ngIf="oidcForm && app?.apiConfig" class="btn-container">
<button class="submit-button" (click)="saveAPIApp()" color="primary"
[disabled]="apiForm.invalid || !canWrite" mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
<div *ngIf="currentAuthMethod == 'BASIC'">
<button [disabled]="!canWrite" mat-stroked-button
(click)="regenerateAPIClientSecret()">{{'APP.API.REGENERATESECRET' | translate}}</button>

View File

@ -27,8 +27,8 @@
</cnsl-form-field>
</div>
<div class="btn-container">
<button color="primary" [disabled]="userForm.invalid" type="submit"
mat-raised-button>{{ 'ACTIONS.CREATE' | translate }}</button>
<button color="primary" [disabled]="userForm.invalid" type="submit" mat-raised-button>{{ 'ACTIONS.CREATE' |
translate }}</button>
</div>
</form>
</app-detail-layout>

View File

@ -52,20 +52,22 @@ export class AuthFactorDialogComponent {
});
} else if (type == AuthFactorType.U2F) {
this.authService.addMyMultiFactorU2F().then((u2fresp) => {
const credOptions: CredentialCreationOptions = JSON.parse(atob(u2fresp.key?.publicKey as string));
if (u2fresp.key) {
const credOptions: CredentialCreationOptions = JSON.parse(atob(u2fresp.key?.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);
if (credOptions.publicKey.excludeCredentials) {
credOptions.publicKey.excludeCredentials.map(cred => {
cred.id = _base64ToArrayBuffer(cred.id as any);
return cred;
});
if (credOptions.publicKey?.challenge) {
console.log(credOptions.publicKey);
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
if (credOptions.publicKey.excludeCredentials) {
credOptions.publicKey.excludeCredentials.map(cred => {
cred.id = _base64ToArrayBuffer(cred.id as any);
return cred;
});
}
this.u2fCredentialOptions = credOptions;
}
this.u2fCredentialOptions = credOptions;
}
}, error => {
this.toast.showError(error);
});

View File

@ -74,11 +74,7 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
});
dialogRef.afterClosed().subscribe(done => {
if (done) {
this.getPasswordless();
} else {
this.getPasswordless();
}
this.getPasswordless();
});
}
}

View File

@ -23,7 +23,7 @@ export class DetailFormMachineComponent implements OnInit, OnDestroy {
userName: [{ value: '', disabled: true }, [
Validators.required,
]],
name: [{ value: '', disabled: true }, Validators.required],
name: [{ value: '', disabled: this.disabled }, Validators.required],
description: [{ value: '', disabled: this.disabled }],
});
}

View File

@ -15,11 +15,12 @@
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
<button class="state-button" mat-stroked-button color="warn"
*ngIf="user?.state === UserState.USERSTATE_ACTIVE"
(click)="changeState(UserState.USERSTATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' | translate}}</button>
*ngIf="user?.state === UserState.USER_STATE_ACTIVE"
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
translate}}</button>
<button class="state-button" mat-stroked-button color="warn"
*ngIf="user?.state === UserState.USERSTATE_INACTIVE"
(click)="changeState(UserState.USERSTATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
*ngIf="user?.state === UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
</ng-template>
</div>
@ -76,11 +77,11 @@
(resendEmailVerification)="resendEmailVerification()"
(resendPhoneVerification)="resendPhoneVerification()">
<button pwdAction [disabled]="(canWrite$ | async) == false" (click)="sendSetPasswordNotification()"
mat-stroked-button color="primary"
*ngIf="user.state === UserState.USERSTATE_INITIAL">{{ 'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button>
<button emailAction class="resendemail" *ngIf="user.state == UserState.USERSTATE_INITIAL"
mat-stroked-button color="primary"
(click)="resendInitEmail()">{{'USER.RESENDINITIALEMAIL' | translate}}</button>
mat-stroked-button color="primary" *ngIf="user.state === UserState.USER_STATE_INITIAL">{{
'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button>
<button emailAction class="resendemail" *ngIf="user.state == UserState.USER_STATE_INITIAL"
mat-stroked-button color="primary" (click)="resendInitEmail()">{{'USER.RESENDINITIALEMAIL' |
translate}}</button>
</app-contact>
</app-card>
@ -109,9 +110,10 @@
<span class="second"><span style="display: block;">{{user.preferredLoginName}}</span></span>
</div>
<div class="meta-row">
<span class="first">{{'ORG.PAGES.STATE' | translate}}</span>
<span class="first">{{'ORG.PAGES.STATE' | translate}}</span>
<span *ngIf="user && user.state !== undefined" class="state"
[ngClass]="{'active': user.state === UserState.USERSTATE_ACTIVE, 'inactive': user.state === UserState.USERSTATE_INACTIVE}">{{'USER.DATA.STATE'+user.state | translate}}</span>
[ngClass]="{'active': user.state === UserState.USER_STATE_ACTIVE, 'inactive': user.state === UserState.USER_STATE_INACTIVE}">{{'USER.DATA.STATE'+user.state
| translate}}</span>
</div>
</div>

View File

@ -111,6 +111,7 @@ export class UserDetailComponent implements OnInit {
this.mgmtUserService
.updateMachine(
this.user.id,
this.user.machine.name,
this.user.machine.description)
.then(() => {
this.toast.showInfo('USER.TOAST.SAVED', true);
@ -156,6 +157,14 @@ export class UserDetailComponent implements OnInit {
if (this.user.id && email) {
this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => {
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
if (this.user.state == UserState.USER_STATE_INITIAL) {
this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => {
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
}
if (this.user.human) {
this.user.human.email = new Email().setEmail(email).toObject();
this.refreshUser();

View File

@ -3,7 +3,10 @@
<table class="table" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="type">
<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">
<span *ngIf="mfa.otp !== undefined">OTP (One-Time Password)</span>
<span *ngIf="mfa.u2f !== undefined">U2F (Universal 2nd Factor)</span>
</td>
</ng-container>
<ng-container matColumnDef="attr">

View File

@ -724,6 +724,7 @@
"USERNAME": "Benutzername",
"FIRSTNAME": "Vorname",
"LASTNAME": "Nachname",
"LOGINNAME":"Loginname",
"EMAIL": "E-Mail",
"ROLES": "Rollen",
"USERID": "Benutzer-ID"
@ -857,7 +858,7 @@
"DESCRIPTION":"Geben Sie die korrekte OIDC Konfiguration Ihres Identity Providers an!"
}
},
"TYPES": {
"OWNERTYPES": {
"0":"unknown",
"1":"System",
"2":"Organisation"

View File

@ -722,6 +722,7 @@
"USERNAME": "User Name",
"FIRSTNAME": "First Name",
"LASTNAME": "Last Name",
"LOGINNAME":"Loginname",
"EMAIL": "E-mail",
"ROLES": "Roles",
"USERID":"User ID"
@ -855,7 +856,7 @@
"DESCRIPTION":"Provide the correct OIDC Configuration for your identity Provider below!"
}
},
"TYPES": {
"OWNERTYPES": {
"0":"unknown",
"1":"System",
"2":"Organisation"