diff --git a/console/angular.json b/console/angular.json index 278498ccd7..5564b2c428 100644 --- a/console/angular.json +++ b/console/angular.json @@ -63,7 +63,7 @@ { "type": "initial", "maximumWarning": "8mb", - "maximumError": "9mb" + "maximumError": "10mb" }, { "type": "anyComponentStyle", diff --git a/console/package.json b/console/package.json index fcf3a4bbf8..2c1d38da1b 100644 --- a/console/package.json +++ b/console/package.json @@ -24,6 +24,8 @@ "@angular/platform-browser-dynamic": "^16.2.5", "@angular/router": "^16.2.5", "@angular/service-worker": "^16.2.5", + "@connectrpc/connect": "^2.0.0", + "@connectrpc/connect-web": "^2.0.0", "@ctrl/ngx-codemirror": "^6.1.0", "@fortawesome/angular-fontawesome": "^0.13.0", "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -31,6 +33,8 @@ "@grpc/grpc-js": "^1.11.2", "@netlify/framework-info": "^9.8.13", "@ngx-translate/core": "^15.0.0", + "@zitadel/client": "^1.0.6", + "@zitadel/proto": "^1.0.3", "angular-oauth2-oidc": "^15.0.1", "angularx-qrcode": "^16.0.0", "buffer": "^6.0.3", diff --git a/console/src/app/app.component.html b/console/src/app/app.component.html index 5b31b33dc4..18c5c72501 100644 --- a/console/src/app/app.component.html +++ b/console/src/app/app.component.html @@ -1,5 +1,5 @@
- + { // We use navigateByUrl as our urls may have queryParams - this.router.navigateByUrl(currentUrl); + this.router.navigateByUrl(currentUrl).then(); }); } @@ -283,18 +283,16 @@ export class AppComponent implements OnDestroy { this.translate.addLangs(supportedLanguages); this.translate.setDefaultLang(fallbackLanguage); - this.authService.userSubject.pipe(takeUntil(this.destroy$)).subscribe((userprofile) => { - if (userprofile) { - const cropped = navigator.language.split('-')[0] ?? fallbackLanguage; - const fallbackLang = cropped.match(supportedLanguagesRegexp) ? cropped : fallbackLanguage; + this.authService.user.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((userprofile) => { + const cropped = navigator.language.split('-')[0] ?? fallbackLanguage; + const fallbackLang = cropped.match(supportedLanguagesRegexp) ? cropped : fallbackLanguage; - const lang = userprofile?.human?.profile?.preferredLanguage.match(supportedLanguagesRegexp) - ? userprofile.human.profile?.preferredLanguage - : fallbackLang; - this.translate.use(lang); - this.language = lang; - this.document.documentElement.lang = lang; - } + const lang = userprofile?.human?.profile?.preferredLanguage.match(supportedLanguagesRegexp) + ? userprofile.human.profile?.preferredLanguage + : fallbackLang; + this.translate.use(lang); + this.language = lang; + this.document.documentElement.lang = lang; }); } @@ -308,7 +306,7 @@ export class AppComponent implements OnDestroy { } private setFavicon(theme: string): void { - this.authService.labelpolicy.pipe(takeUntil(this.destroy$)).subscribe((lP) => { + this.authService.labelpolicy$.pipe(startWith(undefined), takeUntil(this.destroy$)).subscribe((lP) => { if (theme === 'dark-theme' && lP?.iconUrlDark) { // Check if asset url is stable, maybe it was deleted but still wasn't applied fetch(lP.iconUrlDark).then((response) => { diff --git a/console/src/app/components/features/features.component.html b/console/src/app/components/features/features.component.html index e663569210..fdd397084a 100644 --- a/console/src/app/components/features/features.component.html +++ b/console/src/app/components/features/features.component.html @@ -403,6 +403,34 @@ 'SETTING.FEATURES.OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION' | translate }}
+ +
+ {{ 'SETTING.FEATURES.CONSOLEUSEV2USERAPI' | translate }} +
+ + +
+ {{ 'SETTING.FEATURES.STATES.DISABLED' | translate }} +
+
+ +
+ {{ 'SETTING.FEATURES.STATES.ENABLED' | translate }} +
+
+
+
+ {{ + 'SETTING.FEATURES.CONSOLEUSEV2USERAPI_DESCRIPTION' | translate + }} +
diff --git a/console/src/app/components/features/features.component.ts b/console/src/app/components/features/features.component.ts index 327e9d2792..0f8ff761f6 100644 --- a/console/src/app/components/features/features.component.ts +++ b/console/src/app/components/features/features.component.ts @@ -16,13 +16,14 @@ import { InfoSectionModule } from 'src/app/modules/info-section/info-section.mod import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { Event } from 'src/app/proto/generated/zitadel/event_pb'; import { Source } from 'src/app/proto/generated/zitadel/feature/v2beta/feature_pb'; -import { - GetInstanceFeaturesResponse, - SetInstanceFeaturesRequest, -} from 'src/app/proto/generated/zitadel/feature/v2beta/instance_pb'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { FeatureService } from 'src/app/services/feature.service'; import { ToastService } from 'src/app/services/toast.service'; +import { + GetInstanceFeaturesResponse, + SetInstanceFeaturesRequest, +} from '../../proto/generated/zitadel/feature/v2/instance_pb'; +import { withIdentifier } from 'codelyzer/util/astQuery'; enum ToggleState { ENABLED = 'ENABLED', @@ -39,6 +40,7 @@ type ToggleStates = { oidcTokenExchange?: FeatureState; actions?: FeatureState; oidcSingleV1SessionTermination?: FeatureState; + consoleUseV2UserApi?: FeatureState; }; @Component({ @@ -142,6 +144,7 @@ export class FeaturesComponent implements OnDestroy { ); changed = true; } + req.setConsoleUseV2UserApi(this.toggleStates?.consoleUseV2UserApi?.state === ToggleState.ENABLED); if (changed) { this.featureService @@ -232,6 +235,10 @@ export class FeaturesComponent implements OnDestroy { ? ToggleState.ENABLED : ToggleState.DISABLED, }, + consoleUseV2UserApi: { + source: this.featureData.consoleUseV2UserApi?.source || Source.SOURCE_INSTANCE, + state: this.featureData.consoleUseV2UserApi?.enabled ? ToggleState.ENABLED : ToggleState.DISABLED, + }, }; }); } diff --git a/console/src/app/directives/has-role/has-role.directive.ts b/console/src/app/directives/has-role/has-role.directive.ts index b58e1f3a10..9ba21c1dd2 100644 --- a/console/src/app/directives/has-role/has-role.directive.ts +++ b/console/src/app/directives/has-role/has-role.directive.ts @@ -1,18 +1,17 @@ -import { Directive, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'; -import { Subject, takeUntil } from 'rxjs'; +import { DestroyRef, Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ selector: '[cnslHasRole]', }) -export class HasRoleDirective implements OnDestroy { - private destroy$: Subject = new Subject(); +export class HasRoleDirective { private hasView: boolean = false; @Input() public set hasRole(roles: string[] | RegExp[] | undefined) { if (roles && roles.length > 0) { this.authService .isAllowed(roles) - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((isAllowed) => { if (isAllowed && !this.hasView) { if (this.viewContainerRef.length !== 0) { @@ -38,10 +37,6 @@ export class HasRoleDirective implements OnDestroy { private authService: GrpcAuthService, protected templateRef: TemplateRef, protected viewContainerRef: ViewContainerRef, + private readonly destroyRef: DestroyRef, ) {} - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } } diff --git a/console/src/app/modules/accounts-card/accounts-card.component.ts b/console/src/app/modules/accounts-card/accounts-card.component.ts index 617a41bf6d..2676a5bcf5 100644 --- a/console/src/app/modules/accounts-card/accounts-card.component.ts +++ b/console/src/app/modules/accounts-card/accounts-card.component.ts @@ -4,6 +4,7 @@ import { AuthConfig } from 'angular-oauth2-oidc'; import { Session, User, UserState } from 'src/app/proto/generated/zitadel/user_pb'; import { AuthenticationService } from 'src/app/services/authentication.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { toSignal } from '@angular/core/rxjs-interop'; @Component({ selector: 'cnsl-accounts-card', @@ -18,6 +19,8 @@ export class AccountsCardComponent implements OnInit { public sessions: Session.AsObject[] = []; public loadingUsers: boolean = false; public UserState: any = UserState; + private labelpolicy = toSignal(this.userService.labelpolicy$, { initialValue: undefined }); + constructor( public authService: AuthenticationService, private router: Router, @@ -68,7 +71,7 @@ export class AccountsCardComponent implements OnInit { } public logout(): void { - const lP = JSON.stringify(this.userService.labelpolicy.getValue()); + const lP = JSON.stringify(this.labelpolicy()); localStorage.setItem('labelPolicyOnSignout', lP); this.authService.signout(); diff --git a/console/src/app/modules/footer/footer.component.html b/console/src/app/modules/footer/footer.component.html index 26d863d129..b9eda2d7db 100644 --- a/console/src/app/modules/footer/footer.component.html +++ b/console/src/app/modules/footer/footer.component.html @@ -1,6 +1,6 @@