From 437f37c521ec6dc77f150b376a9468f53f36a4a4 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 19 May 2021 19:23:07 +0200 Subject: [PATCH 1/2] fix: add model for api keys, fix toast, binding --- .../client-keys/client-keys.component.ts | 234 +++--- .../show-key-dialog.component.html | 3 +- .../show-key-dialog.component.ts | 39 +- .../apps/app-create/app-create.component.html | 2 +- .../apps/app-create/app-create.component.ts | 740 +++++++++--------- .../apps/app-detail/app-detail.component.ts | 3 +- console/src/assets/i18n/de.json | 1 + console/src/assets/i18n/en.json | 1 + 8 files changed, 513 insertions(+), 510 deletions(-) diff --git a/console/src/app/modules/client-keys/client-keys.component.ts b/console/src/app/modules/client-keys/client-keys.component.ts index 8e1cf0f510c..db593f166ad 100644 --- a/console/src/app/modules/client-keys/client-keys.component.ts +++ b/console/src/app/modules/client-keys/client-keys.component.ts @@ -16,128 +16,128 @@ import { ToastService } from 'src/app/services/toast.service'; import { PageEvent, PaginatorComponent } from '../paginator/paginator.component'; @Component({ - selector: 'app-client-keys', - templateUrl: './client-keys.component.html', - styleUrls: ['./client-keys.component.scss'], + selector: 'app-client-keys', + templateUrl: './client-keys.component.html', + styleUrls: ['./client-keys.component.scss'], }) export class ClientKeysComponent implements OnInit { - @Input() projectId!: string; - @Input() appId!: string; - - @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; - public dataSource: MatTableDataSource = new MatTableDataSource(); - public selection: SelectionModel = new SelectionModel(true, []); - public keyResult!: ListAppKeysResponse.AsObject; - private loadingSubject: BehaviorSubject = new BehaviorSubject(false); - public loading$: Observable = this.loadingSubject.asObservable(); - @Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions']; - - @Output() public changedSelection: EventEmitter> = new EventEmitter(); - - constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog, - private toast: ToastService) { - this.selection.changed.subscribe(() => { - this.changedSelection.emit(this.selection.selected); - }); - } - - public ngOnInit(): void { - this.getData(10, 0); - } - - - public isAllSelected(): boolean { - const numSelected = this.selection.selected.length; - const numRows = this.dataSource.data.length; - return numSelected === numRows; - } - - public masterToggle(): void { - this.isAllSelected() ? - this.selection.clear() : - this.dataSource.data.forEach(row => this.selection.select(row)); - } - - - public changePage(event: PageEvent): void { - this.getData(event.pageSize, event.pageIndex * event.pageSize); - } - - 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); - }).catch(error => { - this.toast.showError(error); - }); - } + @Input() projectId!: string; + @Input() appId!: string; + + @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; + public dataSource: MatTableDataSource = new MatTableDataSource(); + public selection: SelectionModel = new SelectionModel(true, []); + public keyResult!: ListAppKeysResponse.AsObject; + private loadingSubject: BehaviorSubject = new BehaviorSubject(false); + public loading$: Observable = this.loadingSubject.asObservable(); + @Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions']; + + @Output() public changedSelection: EventEmitter> = new EventEmitter(); + + constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog, + private toast: ToastService) { + this.selection.changed.subscribe(() => { + this.changedSelection.emit(this.selection.selected); + }); + } + + public ngOnInit(): void { + this.getData(10, 0); + } + + + public isAllSelected(): boolean { + const numSelected = this.selection.selected.length; + const numRows = this.dataSource.data.length; + return numSelected === numRows; + } + + public masterToggle(): void { + this.isAllSelected() ? + this.selection.clear() : + this.dataSource.data.forEach(row => this.selection.select(row)); + } + + + public changePage(event: PageEvent): void { + this.getData(event.pageSize, event.pageIndex * event.pageSize); + } + + 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); + }).catch(error => { + this.toast.showError(error); + }); + } + + public openAddKey(): void { + const dialogRef = this.dialog.open(AddKeyDialogComponent, { + data: {}, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp) { + const type: KeyType = resp.type; + + let date: Timestamp | undefined; + + if (resp.date as Moment) { + const ts = new Timestamp(); + const milliseconds = resp.date.toDate().getTime(); + const seconds = Math.abs(milliseconds / 1000); + const nanos = (milliseconds - seconds * 1000) * 1000 * 1000; + ts.setSeconds(seconds); + ts.setNanos(nanos); + date = ts; + } - public openAddKey(): void { - const dialogRef = this.dialog.open(AddKeyDialogComponent, { - data: {}, - width: '400px', - }); - - dialogRef.afterClosed().subscribe(resp => { - if (resp) { - const type: KeyType = resp.type; - - let date: Timestamp | undefined; - - if (resp.date as Moment) { - const ts = new Timestamp(); - const milliseconds = resp.date.toDate().getTime(); - const seconds = Math.abs(milliseconds / 1000); - const nanos = (milliseconds - seconds * 1000) * 1000 * 1000; - ts.setSeconds(seconds); - ts.setNanos(nanos); - date = ts; - } - - if (type) { - return this.mgmtService.addAppKey( - this.projectId, - this.appId, - type, - date ? date : undefined, - ).then((response) => { - if (response) { - setTimeout(() => { - this.refreshPage(); - }, 1000); - - this.dialog.open(ShowKeyDialogComponent, { - data: { - key: response, - type: AddKeyDialogType.AUTHNKEY, - }, - width: '400px', - }); - } - }).catch((error: any) => { - this.toast.showError(error); - }); - } + if (type) { + return this.mgmtService.addAppKey( + this.projectId, + this.appId, + type, + date ? date : undefined, + ).then((response) => { + if (response) { + setTimeout(() => { + this.refreshPage(); + }, 1000); + + this.dialog.open(ShowKeyDialogComponent, { + data: { + key: response, + type: AddKeyDialogType.AUTHNKEY, + }, + width: '400px', + }); } - }); - } - - private async getData(limit: number, offset: number): Promise { - this.loadingSubject.next(true); - if (this.projectId && this.appId) { - this.mgmtService.listAppKeys(this.projectId, this.appId, limit, offset).then(resp => { - this.keyResult = resp; - this.dataSource.data = this.keyResult.resultList; - this.loadingSubject.next(false); - }).catch((error: any) => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); + }).catch((error: any) => { + this.toast.showError(error); + }); } + } + }); + } + + private async getData(limit: number, offset: number): Promise { + this.loadingSubject.next(true); + if (this.projectId && this.appId) { + this.mgmtService.listAppKeys(this.projectId, this.appId, limit, offset).then(resp => { + this.keyResult = resp; + this.dataSource.data = this.keyResult.resultList; + this.loadingSubject.next(false); + }).catch((error: any) => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); } + } - public refreshPage(): void { - this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize); - } + public refreshPage(): void { + this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize); + } } diff --git a/console/src/app/modules/show-key-dialog/show-key-dialog.component.html b/console/src/app/modules/show-key-dialog/show-key-dialog.component.html index 0064ae1c3b5..bf253babc21 100644 --- a/console/src/app/modules/show-key-dialog/show-key-dialog.component.html +++ b/console/src/app/modules/show-key-dialog/show-key-dialog.component.html @@ -4,7 +4,8 @@

{{'USER.MACHINE.ID' | translate}}

-

{{keyResponse?.keyId}}

+

{{$any(keyResponse)?.id}}

+

{{$any(keyResponse)?.keyId}}

diff --git a/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts b/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts index 531f8de6aab..d56d29fbc1f 100644 --- a/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts +++ b/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts @@ -1,30 +1,31 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { saveAs } from 'file-saver'; -import { AddMachineKeyResponse } from 'src/app/proto/generated/zitadel/management_pb'; +import { AddAppKeyResponse, AddMachineKeyResponse } from 'src/app/proto/generated/zitadel/management_pb'; @Component({ - selector: 'app-show-key-dialog', - templateUrl: './show-key-dialog.component.html', - styleUrls: ['./show-key-dialog.component.scss'], + selector: 'app-show-key-dialog', + templateUrl: './show-key-dialog.component.html', + styleUrls: ['./show-key-dialog.component.scss'], }) export class ShowKeyDialogComponent { - public keyResponse!: AddMachineKeyResponse.AsObject; + public keyResponse!: AddMachineKeyResponse.AsObject | AddAppKeyResponse.AsObject; - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - ) { - this.keyResponse = data.key; - } + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + ) { + this.keyResponse = data.key; + } - public saveFile(): void { - const json = atob(this.keyResponse.keyDetails.toString()); - const blob = new Blob([json], { type: 'text/plain;charset=utf-8' }); - saveAs(blob, `${this.keyResponse.keyId}.json`); - } + public saveFile(): void { + const json = atob(this.keyResponse.keyDetails.toString()); + const blob = new Blob([json], { type: 'text/plain;charset=utf-8' }); + const name = (this.keyResponse as AddMachineKeyResponse.AsObject).keyId ? (this.keyResponse as AddMachineKeyResponse.AsObject).keyId : (this.keyResponse as AddAppKeyResponse.AsObject).id; + saveAs(blob, `${name}.json`); + } - public closeDialog(): void { - this.dialogRef.close(false); - } + public closeDialog(): void { + this.dialogRef.close(false); + } } diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.html b/console/src/app/pages/projects/apps/app-create/app-create.component.html index 6d12c2bebbd..14ebe03196a 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.html +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.html @@ -180,7 +180,7 @@

{{'APP.PAGES.CREATE_OIDC_DESC_TITLE' | translate}}

- {{'APP.API.AUTHMETHOD.'+oidcAppRequest?.authMethodType | translate}} + {{'APP.API.AUTHMETHOD.'+apiAppRequest?.authMethodType | translate}}
diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.ts b/console/src/app/pages/projects/apps/app-create/app-create.component.ts index 27a25e7ae9a..d922f86a0e6 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.ts +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.ts @@ -9,405 +9,405 @@ import { Subject, Subscription } from 'rxjs'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component'; import { - APIAuthMethodType, - OIDCAppType, - OIDCAuthMethodType, - OIDCGrantType, - OIDCResponseType, + APIAuthMethodType, + OIDCAppType, + OIDCAuthMethodType, + OIDCGrantType, + OIDCResponseType, } from 'src/app/proto/generated/zitadel/app_pb'; import { - AddAPIAppRequest, - AddAPIAppResponse, - AddOIDCAppRequest, - AddOIDCAppResponse, + AddAPIAppRequest, + AddAPIAppResponse, + AddOIDCAppRequest, + AddOIDCAppResponse, } from 'src/app/proto/generated/zitadel/management_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component'; import { - BASIC_AUTH_METHOD, - CODE_METHOD, - getPartialConfigFromAuthMethod, - IMPLICIT_METHOD, - PK_JWT_METHOD, - PKCE_METHOD, - POST_METHOD, + BASIC_AUTH_METHOD, + CODE_METHOD, + getPartialConfigFromAuthMethod, + IMPLICIT_METHOD, + PK_JWT_METHOD, + PKCE_METHOD, + POST_METHOD, } from '../authmethods'; import { API_TYPE, AppCreateType, NATIVE_TYPE, RadioItemAppType, USER_AGENT_TYPE, WEB_TYPE } from '../authtypes'; @Component({ - selector: 'app-app-create', - templateUrl: './app-create.component.html', - styleUrls: ['./app-create.component.scss'], + selector: 'app-app-create', + templateUrl: './app-create.component.html', + styleUrls: ['./app-create.component.scss'], }) export class AppCreateComponent implements OnInit, OnDestroy { - private subscription?: Subscription; - private destroyed$: Subject = new Subject(); - public devmode: boolean = false; - public projectId: string = ''; - public loading: boolean = false; - - public oidcAppRequest: AddOIDCAppRequest.AsObject = new AddOIDCAppRequest().toObject(); - public apiAppRequest: AddAPIAppRequest.AsObject = new AddAPIAppRequest().toObject(); - - public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [ - { type: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE, checked: false, disabled: false }, - { type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN, checked: false, disabled: false }, - { type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN, checked: false, disabled: false }, - ]; - - public oidcAppTypes: OIDCAppType[] = [ - OIDCAppType.OIDC_APP_TYPE_WEB, - OIDCAppType.OIDC_APP_TYPE_NATIVE, - OIDCAppType.OIDC_APP_TYPE_USER_AGENT, - ]; - public appTypes: any = [ - WEB_TYPE, - NATIVE_TYPE, - USER_AGENT_TYPE, - API_TYPE, + private subscription?: Subscription; + private destroyed$: Subject = new Subject(); + public devmode: boolean = false; + public projectId: string = ''; + public loading: boolean = false; + + public oidcAppRequest: AddOIDCAppRequest.AsObject = new AddOIDCAppRequest().toObject(); + public apiAppRequest: AddAPIAppRequest.AsObject = new AddAPIAppRequest().toObject(); + + public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [ + { type: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE, checked: false, disabled: false }, + { type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN, checked: false, disabled: false }, + { type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN, checked: false, disabled: false }, + ]; + + public oidcAppTypes: OIDCAppType[] = [ + OIDCAppType.OIDC_APP_TYPE_WEB, + OIDCAppType.OIDC_APP_TYPE_NATIVE, + OIDCAppType.OIDC_APP_TYPE_USER_AGENT, + ]; + public appTypes: any = [ + WEB_TYPE, + NATIVE_TYPE, + USER_AGENT_TYPE, + API_TYPE, + ]; + + public authMethods: RadioItemAuthType[] = [ + PKCE_METHOD, + CODE_METHOD, + PK_JWT_METHOD, + POST_METHOD, + ]; + + // set to oidc first + public authMethodTypes: { + type: OIDCAuthMethodType | APIAuthMethodType, + checked: boolean, + disabled: boolean; + api?: boolean; + oidc?: boolean; + }[] = [ + { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true }, + { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true }, + { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true }, ]; - public authMethods: RadioItemAuthType[] = [ - PKCE_METHOD, - CODE_METHOD, - PK_JWT_METHOD, - POST_METHOD, + // stepper + firstFormGroup!: FormGroup; + secondFormGroup!: FormGroup; + + // devmode + public form!: FormGroup; + + public AppCreateType: any = AppCreateType; + public OIDCAppType: any = OIDCAppType; + public OIDCGrantType: any = OIDCGrantType; + public OIDCAuthMethodType: any = OIDCAuthMethodType; + + public oidcGrantTypes: { + type: OIDCGrantType, + checked: boolean, + disabled: boolean, + }[] = [ + { type: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, checked: true, disabled: false }, + { type: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT, checked: false, disabled: true }, + // { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true }, + // TODO show when implemented ]; - // set to oidc first - public authMethodTypes: { - type: OIDCAuthMethodType | APIAuthMethodType, - checked: boolean, - disabled: boolean; - api?: boolean; - oidc?: boolean; - }[] = [ - { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true }, - { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true }, - { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true }, - ]; - - // stepper - firstFormGroup!: FormGroup; - secondFormGroup!: FormGroup; - - // devmode - public form!: FormGroup; - - public AppCreateType: any = AppCreateType; - public OIDCAppType: any = OIDCAppType; - public OIDCGrantType: any = OIDCGrantType; - public OIDCAuthMethodType: any = OIDCAuthMethodType; - - public oidcGrantTypes: { - type: OIDCGrantType, - checked: boolean, - disabled: boolean, - }[] = [ - { type: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, checked: true, disabled: false }, - { type: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT, checked: false, disabled: true }, - // { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true }, - // TODO show when implemented - ]; - - public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; - public requestRedirectValuesSubject$: Subject = new Subject(); - - constructor( - private router: Router, - private route: ActivatedRoute, - private toast: ToastService, - private dialog: MatDialog, - private mgmtService: ManagementService, - private fb: FormBuilder, - private _location: Location, - ) { - this.form = this.fb.group({ - name: ['', [Validators.required]], - responseTypesList: ['', [Validators.required]], - grantTypesList: ['', [Validators.required]], - appType: ['', [Validators.required]], - authMethodType: ['', [Validators.required]], - }); - - this.initForm(); - - this.firstFormGroup = this.fb.group({ - name: ['', [Validators.required]], - appType: [WEB_TYPE, [Validators.required]], - }); - - this.firstFormGroup.valueChanges.subscribe(value => { - if (this.firstFormGroup.valid) { - this.oidcAppRequest.name = this.name?.value; - this.apiAppRequest.name = this.name?.value; - - if (this.isStepperOIDC) { - const oidcAppType = (this.appType?.value as RadioItemAppType).oidcAppType; - if (oidcAppType !== undefined) { - this.oidcAppRequest.appType = oidcAppType; - } - - switch (this.oidcAppRequest.appType) { - case OIDCAppType.OIDC_APP_TYPE_NATIVE: - this.authMethods = [ - PKCE_METHOD, - ]; - - // automatically set to PKCE and skip step - this.oidcAppRequest.responseTypesList = [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE]; - this.oidcAppRequest.grantTypesList = [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE]; - this.oidcAppRequest.authMethodType = OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE; - - break; - case OIDCAppType.OIDC_APP_TYPE_WEB: - // PK_JWT_METHOD.recommended = false; - this.authMethods = [ - PKCE_METHOD, - CODE_METHOD, - PK_JWT_METHOD, - POST_METHOD, - ]; - - this.authMethod?.setValue(PKCE_METHOD.key); - break; - case OIDCAppType.OIDC_APP_TYPE_USER_AGENT: - this.authMethods = [ - PKCE_METHOD, - IMPLICIT_METHOD, - ]; - - this.authMethod?.setValue(PKCE_METHOD.key); - break; - } - } else if (this.isStepperAPI) { - // PK_JWT_METHOD.recommended = true; - this.authMethods = [ - PK_JWT_METHOD, - BASIC_AUTH_METHOD, - ]; - - this.authMethod?.setValue(PK_JWT_METHOD.key); - } - } - }); - - this.secondFormGroup = this.fb.group({ - authMethod: [this.authMethods[0].key, [Validators.required]], - }); - this.secondFormGroup.valueChanges.subscribe(form => { - const partialConfig = getPartialConfigFromAuthMethod(form.authMethod); - - if (this.isStepperOIDC && partialConfig && partialConfig.oidc) { - this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList - ?? []; - - this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList - ?? []; - - this.oidcAppRequest.authMethodType = partialConfig.oidc?.authMethodType - ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE; - - } else if (this.isStepperAPI && partialConfig && partialConfig.api) { - this.apiAppRequest.authMethodType = partialConfig.api?.authMethodType - ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC; - } - }); - } - - public ngOnInit(): void { - this.subscription = this.route.params.subscribe(params => this.getData(params)); - } - - public ngOnDestroy(): void { - this.subscription?.unsubscribe(); - this.destroyed$.next(); - } - - public initForm(): void { - this.form.valueChanges.pipe( - takeUntil(this.destroyed$), - debounceTime(150)).subscribe(() => { - this.oidcAppRequest.name = this.formname?.value; - this.apiAppRequest.name = this.formname?.value; - - this.oidcAppRequest.responseTypesList = this.formresponseTypesList?.value; - this.oidcAppRequest.grantTypesList = this.formgrantTypesList?.value; - - this.oidcAppRequest.authMethodType = this.formauthMethodType?.value; - this.apiAppRequest.authMethodType = this.formauthMethodType?.value; - - const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcAppType; - if (oidcAppType !== undefined) { - this.oidcAppRequest.appType = oidcAppType; - } - }); - - this.formappType?.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(() => { - this.setDevFormValidators(); - }); - } - - public setDevFormValidators(): void { - if (this.isDevOIDC) { - const grantTypesControl = new FormControl('', [Validators.required]); - const responseTypesControl = new FormControl('', [Validators.required]); - - this.form.addControl('grantTypesList', grantTypesControl); - this.form.addControl('responseTypesList', responseTypesControl); - - this.authMethodTypes = [ - { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true }, - { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true }, - { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true }, - ]; - this.authMethod?.setValue(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC); - } else if (this.isDevAPI) { - this.form.removeControl('grantTypesList'); - this.form.removeControl('responseTypesList'); - - this.authMethodTypes = [ - { type: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, checked: false, disabled: false, api: true }, - { type: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, api: true }, - ]; - this.authMethod?.setValue(APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT); + public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; + public requestRedirectValuesSubject$: Subject = new Subject(); + + constructor( + private router: Router, + private route: ActivatedRoute, + private toast: ToastService, + private dialog: MatDialog, + private mgmtService: ManagementService, + private fb: FormBuilder, + private _location: Location, + ) { + this.form = this.fb.group({ + name: ['', [Validators.required]], + responseTypesList: ['', [Validators.required]], + grantTypesList: ['', [Validators.required]], + appType: ['', [Validators.required]], + authMethodType: ['', [Validators.required]], + }); + + this.initForm(); + + this.firstFormGroup = this.fb.group({ + name: ['', [Validators.required]], + appType: [WEB_TYPE, [Validators.required]], + }); + + this.firstFormGroup.valueChanges.subscribe(value => { + if (this.firstFormGroup.valid) { + this.oidcAppRequest.name = this.name?.value; + this.apiAppRequest.name = this.name?.value; + + if (this.isStepperOIDC) { + const oidcAppType = (this.appType?.value as RadioItemAppType).oidcAppType; + if (oidcAppType !== undefined) { + this.oidcAppRequest.appType = oidcAppType; + } + + switch (this.oidcAppRequest.appType) { + case OIDCAppType.OIDC_APP_TYPE_NATIVE: + this.authMethods = [ + PKCE_METHOD, + ]; + + // automatically set to PKCE and skip step + this.oidcAppRequest.responseTypesList = [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE]; + this.oidcAppRequest.grantTypesList = [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE]; + this.oidcAppRequest.authMethodType = OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE; + + break; + case OIDCAppType.OIDC_APP_TYPE_WEB: + // PK_JWT_METHOD.recommended = false; + this.authMethods = [ + PKCE_METHOD, + CODE_METHOD, + PK_JWT_METHOD, + POST_METHOD, + ]; + + this.authMethod?.setValue(PKCE_METHOD.key); + break; + case OIDCAppType.OIDC_APP_TYPE_USER_AGENT: + this.authMethods = [ + PKCE_METHOD, + IMPLICIT_METHOD, + ]; + + this.authMethod?.setValue(PKCE_METHOD.key); + break; + } + } else if (this.isStepperAPI) { + // PK_JWT_METHOD.recommended = true; + this.authMethods = [ + PK_JWT_METHOD, + BASIC_AUTH_METHOD, + ]; + + this.authMethod?.setValue(PK_JWT_METHOD.key); } - this.form.updateValueAndValidity(); - } - - public changeStep(event: StepperSelectionEvent): void { - if (event.selectedIndex >= 2) { - this.requestRedirectValuesSubject$.next(); + } + }); + + this.secondFormGroup = this.fb.group({ + authMethod: [this.authMethods[0].key, [Validators.required]], + }); + this.secondFormGroup.valueChanges.subscribe(form => { + const partialConfig = getPartialConfigFromAuthMethod(form.authMethod); + + if (this.isStepperOIDC && partialConfig && partialConfig.oidc) { + this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList + ?? []; + + this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList + ?? []; + + this.oidcAppRequest.authMethodType = partialConfig.oidc?.authMethodType + ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE; + + } else if (this.isStepperAPI && partialConfig && partialConfig.api) { + this.apiAppRequest.authMethodType = partialConfig.api?.authMethodType + ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC; + } + }); + } + + public ngOnInit(): void { + this.subscription = this.route.params.subscribe(params => this.getData(params)); + } + + public ngOnDestroy(): void { + this.subscription?.unsubscribe(); + this.destroyed$.next(); + } + + public initForm(): void { + this.form.valueChanges.pipe( + takeUntil(this.destroyed$), + debounceTime(150)).subscribe(() => { + this.oidcAppRequest.name = this.formname?.value; + this.apiAppRequest.name = this.formname?.value; + + this.oidcAppRequest.responseTypesList = this.formresponseTypesList?.value; + this.oidcAppRequest.grantTypesList = this.formgrantTypesList?.value; + + this.oidcAppRequest.authMethodType = this.formauthMethodType?.value; + this.apiAppRequest.authMethodType = this.formauthMethodType?.value; + + const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcAppType; + if (oidcAppType !== undefined) { + this.oidcAppRequest.appType = oidcAppType; } + }); + + this.formappType?.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(() => { + this.setDevFormValidators(); + }); + } + + public setDevFormValidators(): void { + if (this.isDevOIDC) { + const grantTypesControl = new FormControl('', [Validators.required]); + const responseTypesControl = new FormControl('', [Validators.required]); + + this.form.addControl('grantTypesList', grantTypesControl); + this.form.addControl('responseTypesList', responseTypesControl); + + this.authMethodTypes = [ + { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true }, + { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true }, + { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true }, + ]; + this.authMethod?.setValue(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC); + } else if (this.isDevAPI) { + this.form.removeControl('grantTypesList'); + this.form.removeControl('responseTypesList'); + + this.authMethodTypes = [ + { type: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, checked: false, disabled: false, api: true }, + { type: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, api: true }, + ]; + this.authMethod?.setValue(APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT); } + this.form.updateValueAndValidity(); + } - private async getData({ projectid }: Params): Promise { - this.projectId = projectid; - this.oidcAppRequest.projectId = projectid; - this.apiAppRequest.projectId = projectid; - } - - public close(): void { - this._location.back(); + public changeStep(event: StepperSelectionEvent): void { + if (event.selectedIndex >= 2) { + this.requestRedirectValuesSubject$.next(); } - - public createApp(): void { - const appOIDCCheck = this.devmode ? this.isDevOIDC : this.isStepperOIDC; - const appAPICheck = this.devmode ? this.isDevAPI : this.isStepperAPI; - - if (appOIDCCheck) { - this.requestRedirectValuesSubject$.next(); - - this.loading = true; - this.mgmtService - .addOIDCApp(this.oidcAppRequest) - .then((resp) => { - this.loading = false; - if (resp.clientId || resp.clientSecret) { - this.showSavedDialog(resp); - } else { - this.router.navigate(['projects', this.projectId, 'apps', resp.appId]); - } - }) - .catch(error => { - this.loading = false; - this.toast.showError(error); - }); - } else if (appAPICheck) { - this.loading = true; - this.mgmtService - .addAPIApp(this.apiAppRequest) - .then((resp) => { - this.loading = false; - - if (resp.clientId || resp.clientSecret) { - this.showSavedDialog(resp); - } else { - this.router.navigate(['projects', this.projectId, 'apps', resp.appId]); - } - }) - .catch(error => { - this.loading = false; - this.toast.showError(error); - }); - } - } - - public showSavedDialog(added: AddOIDCAppResponse.AsObject | AddAPIAppResponse.AsObject): void { - let clientSecret = ''; - if (added.clientSecret) { - clientSecret = added.clientSecret; - } - let clientId = ''; - if (added.clientId) { - clientId = added.clientId; - } - const dialogRef = this.dialog.open(AppSecretDialogComponent, { - data: { - clientSecret: clientSecret, - clientId: clientId, - }, + } + + private async getData({ projectid }: Params): Promise { + this.projectId = projectid; + this.oidcAppRequest.projectId = projectid; + this.apiAppRequest.projectId = projectid; + } + + public close(): void { + this._location.back(); + } + + public createApp(): void { + const appOIDCCheck = this.devmode ? this.isDevOIDC : this.isStepperOIDC; + const appAPICheck = this.devmode ? this.isDevAPI : this.isStepperAPI; + + if (appOIDCCheck) { + this.requestRedirectValuesSubject$.next(); + + this.loading = true; + this.mgmtService + .addOIDCApp(this.oidcAppRequest) + .then((resp) => { + this.loading = false; + if (resp.clientId || resp.clientSecret) { + this.showSavedDialog(resp); + } else { + this.router.navigate(['projects', this.projectId, 'apps', resp.appId]); + } + }) + .catch(error => { + this.loading = false; + this.toast.showError(error); }); - - dialogRef.afterClosed().subscribe(() => { - this.router.navigate(['projects', this.projectId, 'apps', added.appId]); + } else if (appAPICheck) { + this.loading = true; + this.mgmtService + .addAPIApp(this.apiAppRequest) + .then((resp) => { + this.loading = false; + + if (resp.clientId || resp.clientSecret) { + this.showSavedDialog(resp); + } else { + this.router.navigate(['projects', this.projectId, 'apps', resp.appId]); + } + }) + .catch(error => { + this.loading = false; + this.toast.showError(error); }); } + } - get name(): AbstractControl | null { - return this.firstFormGroup.get('name'); - } - get appType(): AbstractControl | null { - return this.firstFormGroup.get('appType'); - } - public grantTypeChecked(type: OIDCGrantType): boolean { - return this.oidcGrantTypes.filter(gt => gt.checked).map(gt => gt.type).findIndex(t => t === type) > -1; + public showSavedDialog(added: AddOIDCAppResponse.AsObject | AddAPIAppResponse.AsObject): void { + let clientSecret = ''; + if (added.clientSecret) { + clientSecret = added.clientSecret; } - get responseTypesList(): AbstractControl | null { - return this.secondFormGroup.get('responseTypesList'); - } - get authMethod(): AbstractControl | null { - return this.secondFormGroup.get('authMethod'); - } - - // devmode - - get formname(): AbstractControl | null { - return this.form.get('name'); - } - get formresponseTypesList(): AbstractControl | null { - return this.form.get('responseTypesList'); - } - get formgrantTypesList(): AbstractControl | null { - return this.form.get('grantTypesList'); - } - get formappType(): AbstractControl | null { - return this.form.get('appType'); - } - // get formapplicationType(): AbstractControl | null { - // return this.form.get('applicationType'); - // } - get formauthMethodType(): AbstractControl | null { - return this.form.get('authMethodType'); - } - - get isDevOIDC(): boolean { - return (this.formappType?.value as RadioItemAppType).createType === AppCreateType.OIDC; - } - - get isStepperOIDC(): boolean { - return (this.appType?.value as RadioItemAppType).createType === AppCreateType.OIDC; - } - - get isDevAPI(): boolean { - return (this.formappType?.value as RadioItemAppType).createType === AppCreateType.API; - } - - get isStepperAPI(): boolean { - return (this.appType?.value as RadioItemAppType).createType === AppCreateType.API; + let clientId = ''; + if (added.clientId) { + clientId = added.clientId; } + const dialogRef = this.dialog.open(AppSecretDialogComponent, { + data: { + clientSecret: clientSecret, + clientId: clientId, + }, + }); + + dialogRef.afterClosed().subscribe(() => { + this.router.navigate(['projects', this.projectId, 'apps', added.appId]); + }); + } + + get name(): AbstractControl | null { + return this.firstFormGroup.get('name'); + } + get appType(): AbstractControl | null { + return this.firstFormGroup.get('appType'); + } + public grantTypeChecked(type: OIDCGrantType): boolean { + return this.oidcGrantTypes.filter(gt => gt.checked).map(gt => gt.type).findIndex(t => t === type) > -1; + } + get responseTypesList(): AbstractControl | null { + return this.secondFormGroup.get('responseTypesList'); + } + get authMethod(): AbstractControl | null { + return this.secondFormGroup.get('authMethod'); + } + + // devmode + + get formname(): AbstractControl | null { + return this.form.get('name'); + } + get formresponseTypesList(): AbstractControl | null { + return this.form.get('responseTypesList'); + } + get formgrantTypesList(): AbstractControl | null { + return this.form.get('grantTypesList'); + } + get formappType(): AbstractControl | null { + return this.form.get('appType'); + } + // get formapplicationType(): AbstractControl | null { + // return this.form.get('applicationType'); + // } + get formauthMethodType(): AbstractControl | null { + return this.form.get('authMethodType'); + } + + get isDevOIDC(): boolean { + return (this.formappType?.value as RadioItemAppType).createType === AppCreateType.OIDC; + } + + get isStepperOIDC(): boolean { + return (this.appType?.value as RadioItemAppType).createType === AppCreateType.OIDC; + } + + get isDevAPI(): boolean { + return (this.formappType?.value as RadioItemAppType).createType === AppCreateType.API; + } + + get isStepperAPI(): boolean { + return (this.appType?.value as RadioItemAppType).createType === AppCreateType.API; + } } diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts b/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts index 43db026ab1b..ccc464771be 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts @@ -511,7 +511,7 @@ export class AppDetailComponent implements OnInit, OnDestroy { const config = { api: this.app.apiConfig }; this.currentAuthMethod = this.authMethodFromPartialConfig(config); } - this.toast.showInfo('APP.TOAST.OIDCUPDATED', true); + this.toast.showInfo('APP.TOAST.APIUPDATED', true); }) .catch(error => { this.toast.showError(error); @@ -540,7 +540,6 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true); this.dialog.open(AppSecretDialogComponent, { data: { - // clientId: data.toObject().clientId ?? '', clientSecret: resp.clientSecret, }, width: '400px', diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index bb6ece817f7..2ff7927e48f 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1195,6 +1195,7 @@ "REACTIVATED": "Anwendung reaktiviert.", "DEACTIVATED": "Anwendung deaktiviert.", "OIDCUPDATED": "OIDC-Konfiguration geändert.", + "APIUPDATED":"API Konfiguration geändert.", "UPDATED": "App geändert.", "CLIENTSECRETREGENERATED": "Client Secret generiert.", "DELETED": "App gelöscht.", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index d18448a493d..ee435039603 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1196,6 +1196,7 @@ "REACTIVATED": "Application reactivated.", "DEACTIVATED": "Application deactivated.", "OIDCUPDATED": "OIDC configuration updated.", + "APIUPDATED":"API configuration updated", "UPDATED": "App updated.", "CLIENTSECRETREGENERATED": "client secret generated.", "DELETED": "App deleted.", From 8efd43e4024eccd96a5c2c7d24d4ad8d413b5450 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Thu, 20 May 2021 09:11:48 +0200 Subject: [PATCH 2/2] show api clientid --- .../show-key-dialog/show-key-dialog.component.ts | 4 +++- .../apps/app-detail/app-detail.component.html | 13 +++++++++++++ console/src/assets/i18n/de.json | 3 +++ console/src/assets/i18n/en.json | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts b/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts index d56d29fbc1f..cca040f9406 100644 --- a/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts +++ b/console/src/app/modules/show-key-dialog/show-key-dialog.component.ts @@ -21,7 +21,9 @@ export class ShowKeyDialogComponent { public saveFile(): void { const json = atob(this.keyResponse.keyDetails.toString()); const blob = new Blob([json], { type: 'text/plain;charset=utf-8' }); - const name = (this.keyResponse as AddMachineKeyResponse.AsObject).keyId ? (this.keyResponse as AddMachineKeyResponse.AsObject).keyId : (this.keyResponse as AddAppKeyResponse.AsObject).id; + const name = (this.keyResponse as AddMachineKeyResponse.AsObject).keyId ? + (this.keyResponse as AddMachineKeyResponse.AsObject).keyId : + (this.keyResponse as AddAppKeyResponse.AsObject).id; saveAs(blob, `${name}.json`); } diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index a6251c47a31..fff6f26c972 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -73,6 +73,19 @@

{{app?.name}}

+
+ {{'APP.API.INFO.CLIENTID' | translate}} +
+ {{this.app.apiConfig?.clientId}} + +
+
{{environmentV.key}} diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 2ff7927e48f..1011870cfbb 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1151,6 +1151,9 @@ } }, "API": { + "INFO": { + "CLIENTID": "Client Id" + }, "REGENERATESECRET": "Client Secret neu generieren", "SELECTION": { "TITLE": "API", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index ee435039603..b0992d176a1 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1152,6 +1152,9 @@ } }, "API": { + "INFO": { + "CLIENTID": "Client Id" + }, "REGENERATESECRET": "Regenerate Client Secret", "SELECTION": { "TITLE": "API",