fix(console): user create loading state and i18n, improved app create stepper, invalid token dialog, cleanup, new home (#509)

* fix iam member model

* fix org member model

* fix auth user loading

* copytoclipboard directive

* directive logs, load bar on init, create user

* typo

* welcome section, contributor spinner

* fix home link

* fix stepper flow

* show dialog on invalid token

* fix app table refresh, pin icons light theme

* cleanup contributor

* Update console/src/assets/i18n/en.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

Co-authored-by: Florian Forster <[email protected]>
This commit is contained in:
Max Peintner
2020-07-24 09:48:58 +02:00
committed by GitHub
co-authored by Florian Forster
parent c105bf483b
commit af60b88997
73 changed files with 581 additions and 487 deletions
@@ -41,29 +41,13 @@
</form>
</mat-step>
<mat-step [editable]="true">
<ng-template matStepLabel>{{'APP.OIDC.GRANTSECTION' | translate}}</ng-template>
<p class="step-title">{{'APP.OIDC.GRANTTITLE' | translate}}</p>
<div class="checkbox-container">
<mat-checkbox (change)="changeGrant()" *ngFor="let granttype of oidcGrantTypes" color="primary"
[(ngModel)]="granttype.checked" [disabled]="granttype.disabled">
{{'APP.OIDC.GRANT'+granttype.type | translate}}
</mat-checkbox>
</div>
<div class="actions">
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
<button mat-raised-button color="primary" matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
</div>
</mat-step>
<mat-step [editable]="true" *ngIf="!grantTypeChecked(OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE)">
<mat-step [editable]="true"
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT">
<ng-template matStepLabel>{{'APP.OIDC.RESPONSESECTION' | translate}}</ng-template>
<div class="checkbox-container">
<mat-checkbox *ngFor="let responsetype of oidcResponseTypes | keyvalue" (change)="changeResponseType()"
color="primary" [(ngModel)]="responsetype.checked">
{{'APP.OIDC.RESPONSE'+responsetype.key | translate}}
<mat-checkbox *ngFor="let responsetype of oidcResponseTypes" (change)="changeResponseType()"
color="primary" [(ngModel)]="responsetype.checked" [disabled]="responsetype.disabled">
{{'APP.OIDC.RESPONSE'+responsetype.type | translate}}
</mat-checkbox>
</div>
<div class="actions">
@@ -72,8 +56,8 @@
</div>
</mat-step>
<mat-step [stepControl]="secondFormGroup" [editable]="true"
*ngIf="applicationType?.value !== OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
<mat-step *ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB"
[stepControl]="secondFormGroup" [editable]="true">
<form [formGroup]="secondFormGroup">
<ng-template matStepLabel>{{'APP.OIDC.AUTHMETHODSECTION' | translate}}</ng-template>
@@ -157,19 +141,32 @@
<span class="left">
{{ 'APP.GRANT' | translate }}
</span>
<span class="right">
<span *ngFor="let element of oidcApp.grantTypesList">
<span class="right" *ngIf="oidcApp.grantTypesList?.length > 0">
[<span *ngFor="let element of oidcApp.grantTypesList; index as i">
{{'APP.OIDC.GRANT'+element | translate}}
</span>
{{i < oidcApp.grantTypesList.length - 1 ? ', ': ''}}
</span>]
</span>
</div>
<div class="row">
<span class="left">
{{ 'APP.OIDC.RESPONSE' | translate }}
</span>
<span class="right" *ngIf="oidcApp.responseTypesList?.length > 0">
[<span *ngFor="let element of oidcApp.responseTypesList; index as i">
{{('APP.OIDC.RESPONSE'+element | translate)}}
{{i < oidcApp.responseTypesList.length - 1 ? ', ': ''}}
</span>]
</span>
</div>
<div class="row">
<span class="left">
{{ 'APP.OIDC.AUTHMETHOD' | translate }}
</span>
<span class="right">
<span *ngFor="let element of oidcApp.responseTypesList">
{{'APP.OIDC.RESPONSE'+element | translate}}
<span>
{{'APP.OIDC.AUTHMETHOD'+oidcApp?.authMethodType | translate}}
</span>
</span>
</div>
@@ -178,10 +175,11 @@
<span class="left">
{{ 'APP.OIDC.REDIRECT' | translate }}
</span>
<span class="right">
<span *ngFor="let redirect of oidcApp.redirectUrisList">
<span class="right" *ngIf="oidcApp.redirectUrisList?.length > 0">
[<span *ngFor="let redirect of oidcApp.redirectUrisList; index as i">
{{redirect}}
</span>
{{i < oidcApp.redirectUrisList.length - 1 ? ', ': ''}}
</span>]
</span>
</div>
@@ -189,10 +187,11 @@
<span class="left">
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
</span>
<span class="right">
<span *ngFor="let redirect of oidcApp.postLogoutRedirectUrisList">
<span class="right" *ngIf="oidcApp.postLogoutRedirectUrisList?.length > 0">
[<span *ngFor="let redirect of oidcApp.postLogoutRedirectUrisList; index as i">
{{redirect}}
</span>
{{i < oidcApp.postLogoutRedirectUrisList.length - 1 ? ', ': ''}}
</span>]
</span>
</div>
@@ -14,7 +14,11 @@ p.desc {
}
.container {
padding: 4rem 4rem 2rem 4rem;
padding: 4rem 4rem 2rem 4rem;
mat-progress-bar {
margin-bottom: 1rem;
}
.abort-container {
display: flex;
@@ -32,11 +32,36 @@ export class AppCreateComponent implements OnInit, OnDestroy {
public loading: boolean = false;
public oidcApp: OIDCApplicationCreate.AsObject = new OIDCApplicationCreate().toObject();
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; }[] = [
{ type: OIDCResponseType.OIDCRESPONSETYPE_CODE, checked: false },
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, checked: false },
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, checked: false },
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [
{ type: OIDCResponseType.OIDCRESPONSETYPE_CODE, checked: false, disabled: false },
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, checked: false, disabled: false },
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, checked: false, disabled: false },
];
public oidcAppTypes: OIDCApplicationType[] = [
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
];
public oidcAuthMethodType: { type: OIDCAuthMethodType, checked: boolean, disabled: boolean; }[] = [
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false },
];
// stepper
firstFormGroup!: FormGroup;
secondFormGroup!: FormGroup;
// thirdFormGroup!: FormGroup;
// devmode
public form!: FormGroup;
public OIDCApplicationType: any = OIDCApplicationType;
public OIDCGrantType: any = OIDCGrantType;
public OIDCAuthMethodType: any = OIDCAuthMethodType;
public oidcGrantTypes: {
type: OIDCGrantType,
checked: boolean,
@@ -47,28 +72,6 @@ export class AppCreateComponent implements OnInit, OnDestroy {
// { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true },
// TODO show when implemented
];
public oidcAppTypes: OIDCApplicationType[] = [
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
];
public oidcAuthMethodType: { type: OIDCAuthMethodType, checked: boolean, disabled: boolean; }[] = [
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false },
];
// stepper
firstFormGroup!: FormGroup;
secondFormGroup!: FormGroup;
thirdFormGroup!: FormGroup;
// devmode
public form!: FormGroup;
public OIDCApplicationType: any = OIDCApplicationType;
public OIDCGrantType: any = OIDCGrantType;
public OIDCAuthMethodType: any = OIDCAuthMethodType;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@@ -92,8 +95,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.form.valueChanges.pipe(debounceTime(300)).subscribe((value) => {
this.oidcApp.name = this.formname?.value;
this.oidcApp.applicationType = this.formapplicationType?.value;
this.oidcApp.grantTypesList = this.formgrantTypesList?.value;
this.oidcApp.responseTypesList = this.formresponseTypesList?.value;
this.oidcApp.grantTypesList = this.formgrantTypesList?.value;
this.oidcApp.authMethodType = this.formauthMethodType?.value;
console.log(this.oidcApp);
@@ -103,48 +106,60 @@ export class AppCreateComponent implements OnInit, OnDestroy {
name: ['', [Validators.required]],
applicationType: ['', [Validators.required]],
});
this.firstFormGroup.valueChanges.subscribe(value => {
if (this.firstFormGroup.valid) {
console.log('change firstform', value);
switch (value.applicationType) {
case OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE:
console.log('NATIVE');
this.oidcResponseTypes[0].checked = true;
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE];
this.oidcApp.grantTypesList =
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE];
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE;
break;
case OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB:
console.log('WEB');
this.oidcAuthMethodType[0].disabled = false;
this.oidcAuthMethodType[1].disabled = true; // NONE DISABLED
this.oidcAuthMethodType[2].disabled = false; // POST POSSIBLE
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC); // BASIC DEFAULT
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC;
this.oidcResponseTypes[0].checked = true;
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE];
this.changeResponseType();
this.oidcApp.grantTypesList =
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE];
break;
case OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT:
console.log('USERAGENT');
this.oidcResponseTypes[0].checked = true;
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE];
this.oidcApp.grantTypesList =
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDCGRANTTYPE_IMPLICIT];
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE;
break;
}
this.oidcApp.name = this.name?.value;
this.oidcApp.applicationType = this.applicationType?.value;
}
});
this.secondFormGroup = this.fb.group({
authMethodType: [OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, [Validators.required]],
});
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC;
this.firstFormGroup.valueChanges.subscribe(value => {
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE) {
this.oidcResponseTypes[0].checked = true;
// this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
this.oidcAuthMethodType[1].disabled = true;
}
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB) {
this.oidcResponseTypes[0].checked = true;
this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
this.oidcAuthMethodType[0].disabled = true;
this.oidcAuthMethodType[2].disabled = true;
}
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT) {
this.oidcResponseTypes[0].checked = true;
this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
this.oidcAuthMethodType[0].disabled = true;
this.oidcAuthMethodType[2].disabled = true;
this.oidcGrantTypes[1].disabled = false;
}
this.changeResponseType();
this.changeGrant();
this.oidcApp.name = this.name?.value;
this.oidcApp.applicationType = this.applicationType?.value;
});
this.secondFormGroup.valueChanges.subscribe(value => {
this.oidcApp.authMethodType = this.authMethodType?.value;
this.oidcApp.authMethodType = value.authMethodType;
});
}
@@ -226,14 +241,14 @@ export class AppCreateComponent implements OnInit, OnDestroy {
}
}
changeGrant(): void {
this.oidcApp.grantTypesList = this.oidcGrantTypes.filter(gt => gt.checked).map(gt => gt.type);
}
changeResponseType(): void {
this.oidcApp.responseTypesList = this.oidcResponseTypes.filter(gt => gt.checked).map(gt => gt.type);
}
moreThanOneOption(options: Array<{ type: OIDCGrantType, checked: boolean, disabled: boolean; }>): boolean {
return options.filter(option => option.disabled === false).length > 1;
}
get name(): AbstractControl | null {
return this.firstFormGroup.get('name');
}
@@ -86,7 +86,9 @@
<mat-form-field class="full-width" appearance="outline">
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
<mat-chip-list #chipRedirectList>
<mat-chip *ngFor="let redirect of redirectUrisList" [selectable]="selectable"
<mat-chip *ngFor="let redirect of redirectUrisList" selected
[matTooltip]="!redirect.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
[color]="!redirect.startsWith('https://') ? 'warn': 'green'"
(removed)="remove(redirect, RedirectType.REDIRECT)">
{{redirect}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
@@ -100,8 +102,10 @@
<mat-form-field class="full-width" appearance="outline">
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
<mat-chip-list #chipPostRedirectList>
<mat-chip *ngFor="let redirect of postLogoutRedirectUrisList" [selectable]="selectable"
(removed)="remove(redirect, RedirectType.POSTREDIRECT)">
<mat-chip *ngFor="let redirect of postLogoutRedirectUrisList" selected
(removed)="remove(redirect, RedirectType.POSTREDIRECT)"
[matTooltip]="!redirect.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
[color]="!redirect.startsWith('https://') ? 'warn': 'green'">
{{redirect}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
@@ -84,4 +84,8 @@
border-radius: .5rem;
margin: 0 .5rem;
}
}
mat-chip[color='green'] {
background-color: #56a392 !important;
}
@@ -35,7 +35,6 @@ enum RedirectType {
})
export class AppDetailComponent implements OnInit, OnDestroy {
public errorMessage: string = '';
public selectable: boolean = false;
public removable: boolean = true;
public addOnBlur: boolean = true;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
@@ -219,7 +218,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.projectService
.UpdateOIDCAppConfig(this.projectId, this.app.id, this.app.oidcConfig)
.then((data: OIDCConfig) => {
.then(() => {
this.toast.showInfo('APP.TOAST.OIDCUPDATED', true);
})
.catch(error => {
@@ -7,7 +7,7 @@
<div *ngIf="data.clientSecret" class="flex">
<button color="primary" [disabled]="copied == data.clientSecret" matTooltip="copy to clipboard"
(click)="copytoclipboard(data.clientSecret)" mat-icon-button>
appCopyToClipboard [valueToCopy]="data.clientSecret" (copiedValue)="this.copied = $event" mat-icon-button>
<i *ngIf="copied != data.clientSecret" class="las la-clipboard"></i>
<i *ngIf="copied == data.clientSecret" class="las la-clipboard-check"></i>
</button>
@@ -14,22 +14,4 @@ export class AppSecretDialogComponent {
public closeDialog(): void {
this.dialogRef.close(false);
}
public copytoclipboard(value: string): void {
const selBox = document.createElement('textarea');
selBox.style.position = 'fixed';
selBox.style.left = '0';
selBox.style.top = '0';
selBox.style.opacity = '0';
selBox.value = value;
document.body.appendChild(selBox);
selBox.focus();
selBox.select();
document.execCommand('copy');
document.body.removeChild(selBox);
this.copied = value;
setTimeout(() => {
this.copied = '';
}, 3000);
}
}
@@ -17,6 +17,7 @@ import { MatSelectModule } from '@angular/material/select';
import { MatStepperModule } from '@angular/material/stepper';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { CardModule } from 'src/app/modules/card/card.module';
@@ -55,6 +56,7 @@ import { AppsRoutingModule } from './apps-routing.module';
TranslateModule,
MatStepperModule,
MatRadioModule,
CopyToClipboardModule,
],
exports: [TranslateModule],
})