From b1d61afc8ff07c35ebd5da582dc1da7d25e83b73 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 16 Oct 2020 13:51:52 +0200 Subject: [PATCH] feat(console): seo metatags, fix policy bugs, project options (#844) * add seo service, index meta info * fix policy buttons, refresh * refresh after timeout * loading spinner for login policy, complexity * fix user form, add project role options * authtoken options * seo service lint * style lint * remove duplicate authmethod * en i18n * Update console/src/assets/i18n/en.json Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> --- console/src/app/app.module.ts | 2 + .../modules/idp-table/idp-table.component.ts | 10 ----- .../login-policy/login-policy.component.html | 12 ++++-- .../login-policy/login-policy.component.scss | 4 ++ .../login-policy/login-policy.component.ts | 41 ++++++++++++------- .../login-policy/login-policy.module.ts | 2 + .../org-iam-policy.component.ts | 18 ++++---- .../password-age-policy.component.html | 7 ++-- .../password-age-policy.component.ts | 15 +++++-- .../password-complexity-policy.component.html | 12 ++++-- .../password-complexity-policy.component.scss | 4 ++ .../password-complexity-policy.component.ts | 21 +++++++--- .../password-complexity-policy.module.ts | 2 + .../password-lockout-policy.component.html | 8 ++-- .../password-lockout-policy.component.ts | 4 +- .../iam-policy-grid.component.ts | 18 ++++++-- console/src/app/pages/iam/iam.component.html | 2 +- .../apps/app-detail/app-detail.component.html | 27 +++++++++++- .../apps/app-detail/app-detail.component.scss | 22 ++++++++++ .../apps/app-detail/app-detail.component.ts | 25 +++++++++++ .../owned-project-detail.component.html | 8 ++++ .../owned-project-detail.component.scss | 12 ++++++ .../owned-project-detail.component.ts | 3 +- .../external-idps/external-idps.component.ts | 4 +- .../user-detail/user-detail.component.html | 7 ++-- console/src/app/services/mgmt.service.ts | 9 +++- console/src/app/services/seo.service.ts | 38 +++++++++++++++++ console/src/assets/i18n/de.json | 18 +++++++- console/src/assets/i18n/en.json | 18 +++++++- console/src/index.html | 17 +++++--- 30 files changed, 310 insertions(+), 80 deletions(-) create mode 100644 console/src/app/services/seo.service.ts diff --git a/console/src/app/app.module.ts b/console/src/app/app.module.ts index 5a783d3833..bd79899e2e 100644 --- a/console/src/app/app.module.ts +++ b/console/src/app/app.module.ts @@ -43,6 +43,7 @@ import { GRPC_INTERCEPTORS } from './services/interceptors/grpc-interceptor'; import { I18nInterceptor } from './services/interceptors/i18n.interceptor'; import { OrgInterceptor } from './services/interceptors/org.interceptor'; import { RefreshService } from './services/refresh.service'; +import { SeoService } from './services/seo.service'; import { StatehandlerProcessorService, StatehandlerProcessorServiceImpl } from './services/statehandler-processor.service'; import { StatehandlerService, StatehandlerServiceImpl } from './services/statehandler.service'; import { StorageService } from './services/storage.service'; @@ -167,6 +168,7 @@ const authConfig: AuthConfig = { multi: true, useClass: OrgInterceptor, }, + SeoService, RefreshService, GrpcService, GrpcAuthService, diff --git a/console/src/app/modules/idp-table/idp-table.component.ts b/console/src/app/modules/idp-table/idp-table.component.ts index b691495ed3..a67d971fe5 100644 --- a/console/src/app/modules/idp-table/idp-table.component.ts +++ b/console/src/app/modules/idp-table/idp-table.component.ts @@ -97,19 +97,9 @@ export class IdpTableComponent implements OnInit { private async getData(limit: number, offset: number): Promise { this.loadingSubject.next(true); - // let query: AdminIdpSearchQuery | MgmtIdpSearchQuery; - // if (this.service instanceof AdminService) { - // query = new AdminIdpSearchQuery(); - // query.setKey(AdminIdpSearchKey.IDPSEARCHKEY_IDP_CONFIG_ID); - // } else if (this.service instanceof ManagementService) { - // query = new MgmtIdpSearchQuery(); - // query.setKey(MgmtIdpSearchKey.IDPSEARCHKEY_PROVIDER_TYPE); - // } - this.service.SearchIdps(limit, offset).then(resp => { this.idpResult = resp.toObject(); this.dataSource.data = this.idpResult.resultList; - console.log(this.idpResult.resultList); this.loadingSubject.next(false); }).catch(error => { this.toast.showError(error); diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html index 28baabcf96..cd48c2ffcf 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy.component.html @@ -2,10 +2,14 @@ [title]="'ORG.POLICY.LOGIN_POLICY.TITLECREATE' | translate" [description]="(serviceType==PolicyComponentServiceType.MGMT ? 'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate"> - - diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.scss b/console/src/app/modules/policies/login-policy/login-policy.component.scss index 4010bfe8d8..6834fca26e 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.scss +++ b/console/src/app/modules/policies/login-policy/login-policy.component.scss @@ -3,6 +3,10 @@ margin-top: 0; } +.spinner-wr { + margin: .5rem 0; +} + .content { padding-top: 1rem; display: flex; diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.ts b/console/src/app/modules/policies/login-policy/login-policy.component.ts index 0a7ff87229..7157e1ab3e 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.component.ts @@ -33,9 +33,11 @@ export class LoginPolicyComponent implements OnDestroy { private sub: Subscription = new Subscription(); public service!: ManagementService | AdminService; - PolicyComponentServiceType: any = PolicyComponentServiceType; + public PolicyComponentServiceType: any = PolicyComponentServiceType; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public idps: MgmtIdpProviderView.AsObject[] | AdminIdpProviderView.AsObject[] = []; + + public loading: boolean = false; constructor( private route: ActivatedRoute, private toast: ToastService, @@ -43,7 +45,6 @@ export class LoginPolicyComponent implements OnDestroy { private injector: Injector, ) { this.sub = this.route.data.pipe(switchMap(data => { - console.log(data.serviceType); this.serviceType = data.serviceType; switch (this.serviceType) { case PolicyComponentServiceType.MGMT: @@ -56,15 +57,19 @@ export class LoginPolicyComponent implements OnDestroy { return this.route.params; })).subscribe(() => { - this.getData().then(data => { - if (data) { - this.loginData = data.toObject(); - } - }); - this.getIdps().then(idps => { - console.log(idps); - this.idps = idps; - }); + this.fetchData(); + }); + } + + private fetchData(): void { + this.getData().then(data => { + if (data) { + this.loginData = data.toObject(); + this.loading = false; + } + }); + this.getIdps().then(idps => { + this.idps = idps; }); } @@ -122,6 +127,10 @@ export class LoginPolicyComponent implements OnDestroy { public savePolicy(): void { this.updateData().then(() => { this.toast.showInfo('ORG.POLICY.LOGIN_POLICY.SAVED', true); + this.loading = true; + setTimeout(() => { + this.fetchData(); + }, 2000); }).catch(error => { this.toast.showError(error); }); @@ -131,9 +140,10 @@ export class LoginPolicyComponent implements OnDestroy { if (this.serviceType === PolicyComponentServiceType.MGMT) { (this.service as ManagementService).RemoveLoginPolicy().then(() => { this.toast.showInfo('ORG.POLICY.TOAST.RESETSUCCESS', true); + this.loading = true; setTimeout(() => { - this.getData(); - }, 1000); + this.fetchData(); + }, 2000); }).catch(error => { this.toast.showError(error); }); @@ -151,7 +161,10 @@ export class LoginPolicyComponent implements OnDestroy { dialogRef.afterClosed().subscribe(resp => { if (resp && resp.idp && resp.type) { this.addIdp(resp.idp, resp.type).then(() => { - this.getData(); + this.loading = true; + setTimeout(() => { + this.fetchData(); + }, 2000); }); } }); diff --git a/console/src/app/modules/policies/login-policy/login-policy.module.ts b/console/src/app/modules/policies/login-policy/login-policy.module.ts index 1cf934b87d..1d7498d395 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.module.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.module.ts @@ -5,6 +5,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; @@ -37,6 +38,7 @@ import { LoginPolicyComponent } from './login-policy.component'; DetailLayoutModule, AddIdpDialogModule, IdpTableModule, + MatProgressSpinnerModule, ], }) export class LoginPolicyModule { } diff --git a/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts b/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts index 75fb428f0d..1f7fc96f09 100644 --- a/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts +++ b/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts @@ -20,7 +20,7 @@ import { PolicyComponentServiceType } from '../policy-component-types.enum'; export class OrgIamPolicyComponent implements OnDestroy { @Input() service!: AdminService; private managementService!: ManagementService; - public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; + public serviceType!: PolicyComponentServiceType; public iamData!: AdminOrgIamPolicyView.AsObject | MgmtOrgIamPolicyView.AsObject; @@ -47,11 +47,7 @@ export class OrgIamPolicyComponent implements OnDestroy { } return this.route.params; })).subscribe(_ => { - this.getData().then(data => { - if (data) { - this.iamData = data.toObject(); - } - }); + this.fetchData(); }); } @@ -59,6 +55,14 @@ export class OrgIamPolicyComponent implements OnDestroy { this.sub.unsubscribe(); } + public fetchData(): void { + this.getData().then(data => { + if (data) { + this.iamData = data.toObject(); + } + }); + } + private async getData(): Promise { switch (this.serviceType) { case PolicyComponentServiceType.MGMT: @@ -114,7 +118,7 @@ export class OrgIamPolicyComponent implements OnDestroy { this.adminService.RemoveOrgIamPolicy(this.org.id).then(() => { this.toast.showInfo('ORG.POLICY.TOAST.RESETSUCCESS', true); setTimeout(() => { - this.getData(); + this.fetchData(); }, 1000); }).catch(error => { this.toast.showError(error); diff --git a/console/src/app/modules/policies/password-age-policy/password-age-policy.component.html b/console/src/app/modules/policies/password-age-policy/password-age-policy.component.html index 50bffc0794..7bc43aaafd 100644 --- a/console/src/app/modules/policies/password-age-policy/password-age-policy.component.html +++ b/console/src/app/modules/policies/password-age-policy/password-age-policy.component.html @@ -1,9 +1,8 @@ - - diff --git a/console/src/app/modules/policies/password-age-policy/password-age-policy.component.ts b/console/src/app/modules/policies/password-age-policy/password-age-policy.component.ts index 4b45707df6..6b6ee7fdad 100644 --- a/console/src/app/modules/policies/password-age-policy/password-age-policy.component.ts +++ b/console/src/app/modules/policies/password-age-policy/password-age-policy.component.ts @@ -110,14 +110,20 @@ export class PasswordAgePolicyComponent implements OnDestroy { (this.service as ManagementService).CreatePasswordAgePolicy( this.ageData.maxAgeDays, this.ageData.expireWarnDays, - ).catch(error => { + ).then(() => { + this.toast.showInfo('ORG.POLICY.TOAST.SET', true); + this.getData(); + }).catch(error => { this.toast.showError(error); }); } else { (this.service as ManagementService).UpdatePasswordAgePolicy( this.ageData.maxAgeDays, this.ageData.expireWarnDays, - ).catch(error => { + ).then(() => { + this.toast.showInfo('ORG.POLICY.TOAST.SET', true); + this.getData(); + }).catch(error => { this.toast.showError(error); }); } @@ -126,7 +132,10 @@ export class PasswordAgePolicyComponent implements OnDestroy { (this.service as AdminService).UpdateDefaultPasswordAgePolicy( this.ageData.maxAgeDays, this.ageData.expireWarnDays, - ).catch(error => { + ).then(() => { + this.toast.showInfo('ORG.POLICY.TOAST.SET', true); + this.getData(); + }).catch(error => { this.toast.showError(error); }); break; diff --git a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.html b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.html index c107c76e34..9157f60b87 100644 --- a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.html +++ b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.html @@ -1,12 +1,16 @@ +

{{'ORG.POLICY.DEFAULTLABEL' | translate}}

- - diff --git a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.scss b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.scss index a80b1ae8f7..aad97ba68c 100644 --- a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.scss +++ b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.scss @@ -3,6 +3,10 @@ margin-top: 0; } +.spinner-wr { + margin: .5rem 0; +} + .content { padding-top: 1rem; display: flex; diff --git a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts index d559f97745..2eb8786ec8 100644 --- a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts +++ b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts @@ -23,6 +23,8 @@ export class PasswordComplexityPolicyComponent implements OnDestroy { private sub: Subscription = new Subscription(); public PolicyComponentServiceType: any = PolicyComponentServiceType; + + public loading: boolean = false; constructor( private route: ActivatedRoute, private toast: ToastService, @@ -42,11 +44,18 @@ export class PasswordComplexityPolicyComponent implements OnDestroy { return this.route.params; })).subscribe(() => { - this.getData().then(data => { - if (data) { - this.complexityData = data.toObject(); - } - }); + this.fetchData(); + }); + } + + public fetchData(): void { + this.loading = true; + + this.getData().then(data => { + if (data) { + this.complexityData = data.toObject(); + this.loading = false; + } }); } @@ -69,7 +78,7 @@ export class PasswordComplexityPolicyComponent implements OnDestroy { this.service.removePasswordComplexityPolicy().then(() => { this.toast.showInfo('ORG.POLICY.TOAST.RESETSUCCESS', true); setTimeout(() => { - this.getData(); + this.fetchData(); }, 1000); }).catch(error => { this.toast.showError(error); diff --git a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts index 82da4dd1f2..beb0593aa1 100644 --- a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts +++ b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts @@ -5,6 +5,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; @@ -29,6 +30,7 @@ import { PasswordComplexityPolicyComponent } from './password-complexity-policy. MatTooltipModule, TranslateModule, DetailLayoutModule, + MatProgressSpinnerModule, ], }) export class PasswordComplexityPolicyModule { } diff --git a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html index 9633d01002..1c93f82c8d 100644 --- a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html +++ b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html @@ -2,10 +2,10 @@ [title]="'ORG.POLICY.PWD_LOCKOUT.TITLE' | translate" [description]="'ORG.POLICY.PWD_LOCKOUT.DESCRIPTION' | translate">

{{'ORG.POLICY.DEFAULTLABEL' | translate}}

- - diff --git a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts index 40fe1bd068..aaef63cef7 100644 --- a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts +++ b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts @@ -58,7 +58,6 @@ export class PasswordLockoutPolicyComponent implements OnDestroy { } private getData(): Promise { - switch (this.serviceType) { case PolicyComponentServiceType.MGMT: return (this.service as ManagementService).GetPasswordLockoutPolicy(); @@ -100,6 +99,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy { this.lockoutData.showLockoutFailure, ).then(() => { this.toast.showInfo('ORG.POLICY.TOAST.SET', true); + this.getData(); }).catch(error => { this.toast.showError(error); }); @@ -110,6 +110,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy { this.lockoutData.showLockoutFailure, ).then(() => { this.toast.showInfo('ORG.POLICY.TOAST.SET', true); + this.getData(); }).catch(error => { this.toast.showError(error); }); @@ -119,6 +120,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy { this.lockoutData.showLockoutFailure, ).then(() => { this.toast.showInfo('ORG.POLICY.TOAST.SET', true); + this.getData(); }).catch(error => { this.toast.showError(error); }); diff --git a/console/src/app/pages/iam/iam-policy-grid/iam-policy-grid.component.ts b/console/src/app/pages/iam/iam-policy-grid/iam-policy-grid.component.ts index fc66d6ee1c..45ef94943a 100644 --- a/console/src/app/pages/iam/iam-policy-grid/iam-policy-grid.component.ts +++ b/console/src/app/pages/iam/iam-policy-grid/iam-policy-grid.component.ts @@ -3,6 +3,7 @@ import { PolicyComponentType } from 'src/app/modules/policies/policy-component-t import { DefaultLoginPolicy, DefaultPasswordComplexityPolicyView, OrgIamPolicyView } from 'src/app/proto/generated/admin_pb'; import { PolicyState } from 'src/app/proto/generated/management_pb'; import { AdminService } from 'src/app/services/admin.service'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; @Component({ selector: 'app-iam-policy-grid', @@ -18,14 +19,25 @@ export class IamPolicyGridComponent { public PolicyComponentType: any = PolicyComponentType; constructor( + private authService: GrpcAuthService, private adminService: AdminService, ) { this.getData(); } private getData(): void { - this.adminService.GetDefaultLoginPolicy().then(data => this.loginPolicy = data.toObject()); - this.adminService.GetDefaultOrgIamPolicy().then(data => this.iamPolicy = data.toObject()); - this.adminService.GetDefaultPasswordComplexityPolicy().then(data => this.complexityPolicy = data.toObject()); + + this.authService.isAllowed(['policy.read']).subscribe(allowed => { + if (allowed) { + this.adminService.GetDefaultLoginPolicy().then(data => this.loginPolicy = data.toObject()); + this.adminService.GetDefaultPasswordComplexityPolicy().then(data => this.complexityPolicy = data.toObject()); + } + }); + + this.authService.isAllowed(['iam.policy.read']).subscribe(allowed => { + if (allowed) { + this.adminService.GetDefaultOrgIamPolicy().then(data => this.iamPolicy = data.toObject()); + } + }); } } diff --git a/console/src/app/pages/iam/iam.component.html b/console/src/app/pages/iam/iam.component.html index f818e62401..928640d7c8 100644 --- a/console/src/app/pages/iam/iam.component.html +++ b/console/src/app/pages/iam/iam.component.html @@ -22,4 +22,4 @@ (showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()" [disabled]="false"> - + \ No newline at end of file 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 9225d22d13..ead4807057 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 @@ -95,11 +95,36 @@ +
+ +

{{'APP.OIDC.TOKENSECTIONTITLE' | translate}}

+ + + {{ 'APP.OIDC.TOKENTYPE' | translate }} + + + {{ 'APP.OIDC.TOKENTYPE'+type | translate }} + + + + + + {{'APP.OIDC.ACCESSTOKENROLEASSERTION' | translate}} +

{{'APP.OIDC.ACCESSTOKENROLEASSERTION_DESCRIPTION' | translate}}

+ + {{'APP.OIDC.IDTOKENROLEASSERTION' | translate}} +

{{'APP.OIDC.IDTOKENROLEASSERTION_DESCRIPTION' | translate}}

+ +
+ +

{{'APP.OIDC.REDIRECTSECTIONTITLE' | translate}}

+ {{ 'APP.OIDC.DEVMODE' | translate }} -

{{'APP.OIDC.DEVMODEDESC' | translate}}

+

{{'APP.OIDC.DEVMODEDESC' | translate}}

{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}

diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss b/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss index 4c9e10687d..3cf6017101 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss @@ -60,6 +60,21 @@ } } + .section-title { + margin-bottom: 1.5rem; + } + + .full-width { + flex-basis: 100%; + margin-left: .5rem; + margin-right: .5rem; + } + + .desc { + color: var(--grey); + font-size: 14px; + } + .devmode { flex: 1 1 100%; margin: 1rem .5rem; @@ -115,3 +130,10 @@ .chip[color='green'] { background-color: #56a392 !important; } + +.divider { + flex-basis: 100%; + margin: 1.5rem .5rem; + height: 1px; + background-color: var(--grey); +} 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 fc91c387a8..c805f55bb6 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 @@ -17,6 +17,7 @@ import { OIDCConfig, OIDCGrantType, OIDCResponseType, + OIDCTokenType, ZitadelDocs, } from 'src/app/proto/generated/management_pb'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; @@ -67,6 +68,11 @@ export class AppDetailComponent implements OnInit, OnDestroy { OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, ]; + public oidcTokenTypes: OIDCTokenType[] = [ + OIDCTokenType.OIDCTOKENTYPE_BEARER, + OIDCTokenType.OIDCTOKENTYPE_JWT, + ]; + public AppState: any = AppState; public appNameForm!: FormGroup; public appForm!: FormGroup; @@ -80,6 +86,7 @@ export class AppDetailComponent implements OnInit, OnDestroy { public OIDCApplicationType: any = OIDCApplicationType; public OIDCAuthMethodType: any = OIDCAuthMethodType; + public OIDCTokenType: any = OIDCTokenType; public redirectControl: FormControl = new FormControl({ value: '', disabled: true }); public postRedirectControl: FormControl = new FormControl({ value: '', disabled: true }); @@ -106,6 +113,9 @@ export class AppDetailComponent implements OnInit, OnDestroy { grantTypesList: [{ value: [], disabled: true }], applicationType: [{ value: '', disabled: true }], authMethodType: [{ value: '', disabled: true }], + accessTokenType: [{ value: '', disabled: true }], + accessTokenRoleAssertion: [{ value: false, disabled: true }], + idTokenRoleAssertion: [{ value: false, disabled: true }], }); } @@ -220,6 +230,9 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.app.oidcConfig.redirectUrisList = this.redirectUrisList; this.app.oidcConfig.postLogoutRedirectUrisList = this.postLogoutRedirectUrisList; this.app.oidcConfig.devMode = this.devMode?.value; + this.app.oidcConfig.accessTokenType = this.accessTokenType?.value; + this.app.oidcConfig.accessTokenRoleAssertion = this.accessTokenRoleAssertion?.value; + this.app.oidcConfig.idTokenRoleAssertion = this.idTokenRoleAssertion?.value; this.mgmtService .UpdateOIDCAppConfig(this.projectId, this.app.id, this.app.oidcConfig) @@ -280,4 +293,16 @@ export class AppDetailComponent implements OnInit, OnDestroy { public get devMode(): AbstractControl | null { return this.appForm.get('devMode'); } + + public get accessTokenType(): AbstractControl | null { + return this.appForm.get('accessTokenType'); + } + + public get idTokenRoleAssertion(): AbstractControl | null { + return this.appForm.get('idTokenRoleAssertion'); + } + + public get accessTokenRoleAssertion(): AbstractControl | null { + return this.appForm.get('accessTokenRoleAssertion'); + } } diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html index aa406f0fbd..0ac7ec87ec 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html @@ -78,6 +78,14 @@ +

{{'PROJECT.ROLE.OPTIONS' | translate}}

+ + {{'PROJECT.ROLE.ASSERTION' | translate}} +

{{'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate}}

+ + {{'PROJECT.ROLE.CHECK' | translate}} +

{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}

+
diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.scss b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.scss index b8c350be8b..2ab632c081 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.scss +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.scss @@ -76,3 +76,15 @@ flex-direction: column; } } + +.desc { + color: var(--grey); + font-size: 14px; + margin-bottom: 1.5rem; +} + +.divider { + height: 1px; + background-color: var(--grey); + margin-bottom: 1.5rem; +} diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts index 52d10c9bc0..617896a5b5 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts @@ -191,7 +191,8 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy { } public saveProject(): void { - this.mgmtService.UpdateProject(this.project.projectId, this.project.name).then(() => { + console.log(this.project); + this.mgmtService.UpdateProject(this.project.projectId, this.project).then(() => { this.toast.showInfo('PROJECT.TOAST.UPDATED', true); }).catch(error => { this.toast.showError(error); diff --git a/console/src/app/pages/users/user-detail/external-idps/external-idps.component.ts b/console/src/app/pages/users/user-detail/external-idps/external-idps.component.ts index 9fcd1e1737..b79d3c9918 100644 --- a/console/src/app/pages/users/user-detail/external-idps/external-idps.component.ts +++ b/console/src/app/pages/users/user-detail/external-idps/external-idps.component.ts @@ -105,7 +105,9 @@ export class ExternalIdpsComponent implements OnInit { if (promise) { promise.then(_ => { - this.refreshPage(); + setTimeout(() => { + this.refreshPage(); + }, 1000); }).catch((error: any) => { this.toast.showError(error); }); diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html index d61e6af45d..f5a695c3b5 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html @@ -41,7 +41,7 @@
- + @@ -54,9 +54,8 @@ - + diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index d2ecdc53b4..9aac012446 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -975,10 +975,12 @@ export class ManagementService { return this.grpcService.mgmt.createProject(req); } - public UpdateProject(id: string, name: string): Promise { + public UpdateProject(id: string, projectView: ProjectView.AsObject): Promise { const req = new ProjectUpdateRequest(); - req.setName(name); req.setId(id); + req.setName(projectView.name); + req.setProjectRoleAssertion(projectView.projectRoleAssertion); + req.setProjectRoleCheck(projectView.projectRoleCheck); return this.grpcService.mgmt.updateProject(req); } @@ -1271,6 +1273,9 @@ export class ManagementService { req.setGrantTypesList(oidcConfig.grantTypesList); req.setApplicationType(oidcConfig.applicationType); req.setDevMode(oidcConfig.devMode); + req.setAccessTokenType(oidcConfig.accessTokenType); + req.setAccessTokenRoleAssertion(oidcConfig.accessTokenRoleAssertion); + req.setIdTokenRoleAssertion(oidcConfig.idTokenRoleAssertion); return this.grpcService.mgmt.updateApplicationOIDCConfig(req); } } diff --git a/console/src/app/services/seo.service.ts b/console/src/app/services/seo.service.ts new file mode 100644 index 0000000000..f9abb99c6a --- /dev/null +++ b/console/src/app/services/seo.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; +import { Meta } from '@angular/platform-browser'; + +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root', +}) +export class SeoService { + constructor(private meta: Meta) { } + + public generateTags(config: any): void { + // default values + config = { + title: 'ZITADEL Console', + description: 'Managementplatform for ZITADEL', + image: 'https://www.zitadel.ch/zitadel-social-preview25.png', + slug: '', + ...config, + }; + + this.meta.updateTag({ property: 'og:type', content: 'website' }); + this.meta.updateTag({ property: 'og:site_name', content: 'ZITADEL Console' }); + this.meta.updateTag({ property: 'og:title', content: config.title }); + this.meta.updateTag({ property: 'description', content: config.description }); + this.meta.updateTag({ property: 'og:description', content: config.description }); + if (config.image) { + this.meta.updateTag({ property: 'og:image', content: config.image }); + } + this.meta.updateTag({ property: 'og:url', content: `https://${environment.production ? 'console.zitadel.ch' : 'console.zitadel.dev'}/${config.slug}` }); + + this.meta.updateTag({ property: 'twitter:card', content: 'summary' }); + this.meta.updateTag({ property: 'og:site', content: '@zitadel_ch' }); + this.meta.updateTag({ property: 'og:title', content: config.title }); + this.meta.updateTag({ property: 'og:image', content: 'https://www.zitadel.ch/zitadel-social-preview25.png' }); + this.meta.updateTag({ property: 'og:description', content: config.description }); + } +} diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 091c5a6a1a..ab4da822da 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -648,7 +648,12 @@ "EDITDESCRIPTION": "Gebe die Daten für die zu ändernde Rolle ein.", "DELETE":"Rolle löschen", "CREATIONDATE":"Erstelldatum", - "SELECTGROUPTOOLTIP":"Wähle alle Rollen der Gruppe {{group}} aus." + "SELECTGROUPTOOLTIP":"Wähle alle Rollen der Gruppe {{group}} aus.", + "OPTIONS":"Optionen", + "ASSERTION":"Rollen bei Authentisierung mitschicken", + "ASSERTION_DESCRIPTION":"Rolleninformationen werden der Authentisierung per Token, UserInfo Endpoint oder anderen Methoden bereitgestellt, die in Applikationseinstellungen definiert sind.", + "CHECK":"Rollen bei Authentisierung prüfen", + "CHECK_DESCRIPTION":"Ist das Attribut gesetzt, kann ein Benutzer nur mit einem entsprechenden Rolle authentifiziert werden." }, "TABLE": { "TOTAL": "Einträge gesamt:", @@ -762,6 +767,8 @@ "TYPE":"Anwendungstyp", "GRANT":"Berechtigungstypen", "OIDC": { + "TOKENSECTIONTITLE":"AuthToken Optionen", + "REDIRECTSECTIONTITLE":"Weiterleitungseinstellungen", "PROSWITCH":"Konfigurator überspringen", "NAMEANDTYPESECTION":"Name und Typ", "TITLEFIRST":"Gebe zuerst einen Namen ein.", @@ -802,9 +809,16 @@ "AUTHMETHOD0":"Basic", "AUTHMETHOD1":"Post", "AUTHMETHOD2":"None", + "TOKENTYPE":"Auth Token Typ", + "TOKENTYPE0": "Bearer Token", + "TOKENTYPE1": "JWT", "UNSECUREREDIRECT":"Ich hoffe, Du weisst, was Du tust.", "OVERVIEWSECTION":"Übersicht", - "OVERVIEWTITLE":"Deine Konfiguration ist bereit. Du kannst sie hier nochmals prüfen." + "OVERVIEWTITLE":"Deine Konfiguration ist bereit. Du kannst sie hier nochmals prüfen.", + "ACCESSTOKENROLEASSERTION":"Benutzerrollen dem Access Token hinzufügen", + "ACCESSTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Access Token die Rollen des Authentifizierten Benutzers hinzugefügt.", + "IDTOKENROLEASSERTION":"Benutzerrollen dem Id Token hinzufügen", + "IDTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Id Token die Rollen des Authentifizierten Benutzers hinzugefügt." }, "TOAST": { "REACTIVATED":"Anwendung reaktiviert.", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index b42fff877d..76d977afbb 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -648,7 +648,12 @@ "EDITDESCRIPTION": "Enter the new data for the role.", "DELETE":"Delete Role", "CREATIONDATE":"Created", - "SELECTGROUPTOOLTIP":"Select all Roles of the group {{group}}." + "SELECTGROUPTOOLTIP":"Select all Roles of the group {{group}}.", + "OPTIONS":"Options", + "ASSERTION":"Assert Roles on Authentication.", + "ASSERTION_DESCRIPTION":"Roleinformation is sent as Token, Userinfo endpoint or other type, depending on your application settings.", + "CHECK":"Check roles on Authentication", + "CHECK_DESCRIPTION":"If set, users are only allowed to authenticate if any role is assigned to their account." }, "TABLE": { "TOTAL": "Entries total:", @@ -762,6 +767,8 @@ "TYPE":"Application Type", "GRANT":"Grant Types", "OIDC": { + "TOKENSECTIONTITLE":"AuthToken Options", + "REDIRECTSECTIONTITLE":"Redirect Settings", "PROSWITCH":"I'm a pro. Skip this wizard.", "NAMEANDTYPESECTION":"Name and Type", "TITLEFIRST":"Insert a name first.", @@ -802,9 +809,16 @@ "AUTHMETHOD0":"Basic", "AUTHMETHOD1":"Post", "AUTHMETHOD2":"None", + "TOKENTYPE":"Auth Token Type", + "TOKENTYPE0": "Bearer Token", + "TOKENTYPE1": "JWT", "UNSECUREREDIRECT":"I sure hope you know what you are doing.", "OVERVIEWSECTION":"Overview", - "OVERVIEWTITLE":"You are now done. Review your configuration." + "OVERVIEWTITLE":"You are now done. Review your configuration.", + "ACCESSTOKENROLEASSERTION":"Add user roles to the access token", + "ACCESSTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the access token.", + "IDTOKENROLEASSERTION":"Add user roles to the id token", + "IDTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the id token." }, "TOAST": { "REACTIVATED":"Application reactivated.", diff --git a/console/src/index.html b/console/src/index.html index b8d881f9ff..9d2fd78448 100644 --- a/console/src/index.html +++ b/console/src/index.html @@ -16,14 +16,19 @@ - - - - - + + + + + + - + + + + +