From c16082727252048b33ad8bb50adcf2a718be931b Mon Sep 17 00:00:00 2001 From: Jake Fink Date: Mon, 27 Feb 2023 16:31:55 -0500 Subject: [PATCH] [EC-859] update billing routes for owners of Managed orgs (#4611) * [EC-859] update billing routes for owners of Managed orgs * [EC-859] fix observable in billing tab * [EC-859] update observable name * [EC-859] update reporting and settings observables * [EC-859] add startsWith to reporting observable * [EC-859] async pipe once in settings * [EC-859] create get$ in org service * [EC-859] transition remaining components * [EC-859] add as org to template * [EC-859] add shareReplay to observable to prevent multicasting - future proof get$ on org service * [AC-859] fix missed org --- .../organization-billing-routing.module.ts | 5 +++ .../organization-billing-tab.component.html | 4 +-- .../organization-billing-tab.component.ts | 23 ++++++++++--- .../reporting/reporting.component.html | 10 +++--- .../reporting/reporting.component.ts | 34 +++++++------------ .../settings/settings.component.html | 18 +++++----- .../settings/settings.component.ts | 26 ++++---------- .../organization.service.abstraction.ts | 3 +- .../organization/organization.service.ts | 6 +++- 9 files changed, 66 insertions(+), 63 deletions(-) diff --git a/apps/web/src/app/organizations/billing/organization-billing-routing.module.ts b/apps/web/src/app/organizations/billing/organization-billing-routing.module.ts index b441e033398..e296175edae 100644 --- a/apps/web/src/app/organizations/billing/organization-billing-routing.module.ts +++ b/apps/web/src/app/organizations/billing/organization-billing-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { canAccessBillingTab } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; +import { Organization } from "@bitwarden/common/models/domain/organization"; import { WebPlatformUtilsService } from "../../core/web-platform-utils.service"; import { PaymentMethodComponent } from "../../settings/payment-method.component"; @@ -30,15 +31,19 @@ const routes: Routes = [ { path: "payment-method", component: PaymentMethodComponent, + canActivate: [OrganizationPermissionsGuard], data: { titleId: "paymentMethod", + organizationPermissions: (org: Organization) => org.canManageBilling, }, }, { path: "history", component: OrgBillingHistoryViewComponent, + canActivate: [OrganizationPermissionsGuard], data: { titleId: "billingHistory", + organizationPermissions: (org: Organization) => org.canManageBilling, }, }, ], diff --git a/apps/web/src/app/organizations/billing/organization-billing-tab.component.html b/apps/web/src/app/organizations/billing/organization-billing-tab.component.html index 7f755fccbb9..0c1e4303905 100644 --- a/apps/web/src/app/organizations/billing/organization-billing-tab.component.html +++ b/apps/web/src/app/organizations/billing/organization-billing-tab.component.html @@ -8,7 +8,7 @@ {{ "subscription" | i18n }} ; + + constructor( + private route: ActivatedRoute, + private organizationService: OrganizationService, + private platformUtilsService: PlatformUtilsService + ) {} + + ngOnInit() { + this.showPaymentAndHistory$ = this.route.params.pipe( + switchMap((params) => this.organizationService.get$(params.organizationId)), + map((org) => !this.platformUtilsService.isSelfHost() && org.canManageBilling) + ); } } diff --git a/apps/web/src/app/organizations/reporting/reporting.component.html b/apps/web/src/app/organizations/reporting/reporting.component.html index f7a3ae11a11..1e807b98dba 100644 --- a/apps/web/src/app/organizations/reporting/reporting.component.html +++ b/apps/web/src/app/organizations/reporting/reporting.component.html @@ -1,14 +1,14 @@
-
-
+
+
{{ "reporting" | i18n }}
{{ "eventLogs" | i18n }} @@ -16,14 +16,14 @@ routerLink="reports" class="list-group-item" routerLinkActive="active" - *ngIf="organization.canAccessReports" + *ngIf="org.canAccessReports" > {{ "reports" | i18n }}
-
+
diff --git a/apps/web/src/app/organizations/reporting/reporting.component.ts b/apps/web/src/app/organizations/reporting/reporting.component.ts index 5e70e6a40e3..6b6f88b3427 100644 --- a/apps/web/src/app/organizations/reporting/reporting.component.ts +++ b/apps/web/src/app/organizations/reporting/reporting.component.ts @@ -1,6 +1,6 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { concatMap, Subject, takeUntil } from "rxjs"; +import { map, Observable, shareReplay, startWith, switchMap } from "rxjs"; import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/models/domain/organization"; @@ -9,29 +9,21 @@ import { Organization } from "@bitwarden/common/models/domain/organization"; selector: "app-org-reporting", templateUrl: "reporting.component.html", }) -export class ReportingComponent implements OnInit, OnDestroy { - organization: Organization; - showLeftNav = true; - - private destroy$ = new Subject(); +export class ReportingComponent implements OnInit { + organization$: Observable; + showLeftNav$: Observable; constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {} ngOnInit() { - this.route.params - .pipe( - concatMap(async (params) => { - this.organization = await this.organizationService.get(params.organizationId); - this.showLeftNav = - this.organization.canAccessEventLogs && this.organization.canAccessReports; - }), - takeUntil(this.destroy$) - ) - .subscribe(); - } + this.organization$ = this.route.params.pipe( + switchMap((params) => this.organizationService.get$(params.organizationId)), + shareReplay({ refCount: true, bufferSize: 1 }) + ); - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); + this.showLeftNav$ = this.organization$.pipe( + map((o) => o.canAccessEventLogs && o.canAccessReports), + startWith(true) + ); } } diff --git a/apps/web/src/app/organizations/settings/settings.component.html b/apps/web/src/app/organizations/settings/settings.component.html index 3fbbc3b42c1..146a7203430 100644 --- a/apps/web/src/app/organizations/settings/settings.component.html +++ b/apps/web/src/app/organizations/settings/settings.component.html @@ -3,12 +3,12 @@
{{ "settings" | i18n }}
-
+
{{ "organizationInfo" | i18n }} @@ -16,7 +16,7 @@ routerLink="policies" class="list-group-item" routerLinkActive="active" - *ngIf="organization?.canManagePolicies" + *ngIf="org.canManagePolicies" > {{ "policies" | i18n }} @@ -24,7 +24,7 @@ routerLink="two-factor" class="list-group-item" routerLinkActive="active" - *ngIf="organization?.use2fa && organization?.isOwner" + *ngIf="org.use2fa && org.isOwner" > {{ "twoStepLogin" | i18n }} @@ -32,7 +32,7 @@ routerLink="tools/import" class="list-group-item" routerLinkActive="active" - *ngIf="organization?.canAccessImportExport" + *ngIf="org.canAccessImportExport" > {{ "importData" | i18n }} @@ -40,7 +40,7 @@ routerLink="tools/export" class="list-group-item" routerLinkActive="active" - *ngIf="organization?.canAccessImportExport" + *ngIf="org.canAccessImportExport" > {{ "exportVault" | i18n }} @@ -48,7 +48,7 @@ routerLink="domain-verification" class="list-group-item" routerLinkActive="active" - *ngIf="organization?.canManageDomainVerification" + *ngIf="org?.canManageDomainVerification" > {{ "domainVerification" | i18n }} @@ -56,7 +56,7 @@ routerLink="sso" class="list-group-item" routerLinkActive="active" - *ngIf="organization?.canManageSso" + *ngIf="org.canManageSso" > {{ "singleSignOn" | i18n }} @@ -64,7 +64,7 @@ routerLink="scim" class="list-group-item" routerLinkActive="active" - *ngIf="organization?.canManageScim" + *ngIf="org.canManageScim" > {{ "scim" | i18n }} diff --git a/apps/web/src/app/organizations/settings/settings.component.ts b/apps/web/src/app/organizations/settings/settings.component.ts index 9988e65dd7c..b6ab227490d 100644 --- a/apps/web/src/app/organizations/settings/settings.component.ts +++ b/apps/web/src/app/organizations/settings/settings.component.ts @@ -1,6 +1,6 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { Subject, switchMap, takeUntil } from "rxjs"; +import { Observable, switchMap } from "rxjs"; import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/models/domain/organization"; @@ -9,26 +9,14 @@ import { Organization } from "@bitwarden/common/models/domain/organization"; selector: "app-org-settings", templateUrl: "settings.component.html", }) -export class SettingsComponent implements OnInit, OnDestroy { - organization: Organization; - - private destroy$ = new Subject(); +export class SettingsComponent implements OnInit { + organization$: Observable; constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {} ngOnInit() { - this.route.params - .pipe( - switchMap(async (params) => await this.organizationService.get(params.organizationId)), - takeUntil(this.destroy$) - ) - .subscribe((organization) => { - this.organization = organization; - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); + this.organization$ = this.route.params.pipe( + switchMap((params) => this.organizationService.get$(params.organizationId)) + ); } } diff --git a/libs/common/src/abstractions/organization/organization.service.abstraction.ts b/libs/common/src/abstractions/organization/organization.service.abstraction.ts index ecaeeafab01..a00060c0c27 100644 --- a/libs/common/src/abstractions/organization/organization.service.abstraction.ts +++ b/libs/common/src/abstractions/organization/organization.service.abstraction.ts @@ -32,7 +32,7 @@ export function canAccessReportingTab(org: Organization): boolean { } export function canAccessBillingTab(org: Organization): boolean { - return org.canManageBilling; + return org.isOwner; } export function canAccessOrgAdmin(org: Organization): boolean { @@ -63,6 +63,7 @@ export function isNotProviderUser(org: Organization): boolean { export abstract class OrganizationService { organizations$: Observable; + get$: (id: string) => Observable; get: (id: string) => Organization; getByIdentifier: (identifier: string) => Organization; getAll: (userId?: string) => Promise; diff --git a/libs/common/src/services/organization/organization.service.ts b/libs/common/src/services/organization/organization.service.ts index b0d7791ec26..e2b7e50d4d7 100644 --- a/libs/common/src/services/organization/organization.service.ts +++ b/libs/common/src/services/organization/organization.service.ts @@ -1,4 +1,4 @@ -import { BehaviorSubject, concatMap } from "rxjs"; +import { BehaviorSubject, concatMap, map, Observable } from "rxjs"; import { InternalOrganizationService as InternalOrganizationServiceAbstraction } from "../../abstractions/organization/organization.service.abstraction"; import { StateService } from "../../abstractions/state.service"; @@ -26,6 +26,10 @@ export class OrganizationService implements InternalOrganizationServiceAbstracti .subscribe(); } + get$(id: string): Observable { + return this.organizations$.pipe(map((orgs) => orgs.find((o) => o.id === id))); + } + async getAll(userId?: string): Promise { const organizationsMap = await this.stateService.getOrganizations({ userId: userId }); return Object.values(organizationsMap || {}).map((o) => new Organization(o));