From 010b55d39d3249cb92a2cf04f5006e5109c34c7f Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:10:54 -0400 Subject: [PATCH] Prevent Provider from viewing client organization payment method and billing history (#9442) --- .../organization-layout.component.html | 2 +- .../layouts/organization-layout.component.ts | 29 +++++++++++++-- .../guards/organization-is-unmanaged.guard.ts | 36 +++++++++++++++++++ .../organization-billing-routing.module.ts | 5 +-- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index d1a48a78e1..237e2c6e30 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -52,7 +52,7 @@ *ngIf="canShowBillingTab(organization)" > - + diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts index 47ca0998bb..4383656bee 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts @@ -1,7 +1,7 @@ import { CommonModule } from "@angular/common"; import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, RouterModule } from "@angular/router"; -import { map, mergeMap, Observable, Subject, takeUntil } from "rxjs"; +import { combineLatest, map, mergeMap, Observable, Subject, switchMap, takeUntil } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { @@ -16,7 +16,8 @@ import { OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; +import { PolicyType, ProviderStatusType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -55,9 +56,14 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { organization$: Observable; showPaymentAndHistory$: Observable; hideNewOrgButton$: Observable; + organizationIsUnmanaged$: Observable; private _destroy = new Subject(); + protected consolidatedBillingEnabled$ = this.configService.getFeatureFlag$( + FeatureFlag.EnableConsolidatedBilling, + ); + protected showPaymentMethodWarningBanners$ = this.configService.getFeatureFlag$( FeatureFlag.ShowPaymentMethodWarningBanners, ); @@ -68,6 +74,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { private platformUtilsService: PlatformUtilsService, private configService: ConfigService, private policyService: PolicyService, + private providerService: ProviderService, ) {} async ngOnInit() { @@ -94,6 +101,24 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { ); this.hideNewOrgButton$ = this.policyService.policyAppliesToActiveUser$(PolicyType.SingleOrg); + + const provider$ = this.organization$.pipe( + switchMap((organization) => this.providerService.get$(organization.providerId)), + ); + + this.organizationIsUnmanaged$ = combineLatest([ + this.consolidatedBillingEnabled$, + this.organization$, + provider$, + ]).pipe( + map( + ([consolidatedBillingEnabled, organization, provider]) => + !consolidatedBillingEnabled || + !organization.hasProvider || + !provider || + provider.providerStatus !== ProviderStatusType.Billable, + ), + ); } ngOnDestroy() { diff --git a/apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts b/apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts new file mode 100644 index 0000000000..a915d8f8a6 --- /dev/null +++ b/apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts @@ -0,0 +1,36 @@ +import { inject } from "@angular/core"; +import { ActivatedRouteSnapshot, CanActivateFn } from "@angular/router"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; +import { ProviderStatusType } from "@bitwarden/common/admin-console/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +export const organizationIsUnmanaged: CanActivateFn = async (route: ActivatedRouteSnapshot) => { + const configService = inject(ConfigService); + const organizationService = inject(OrganizationService); + const providerService = inject(ProviderService); + + const consolidatedBillingEnabled = await configService.getFeatureFlag( + FeatureFlag.EnableConsolidatedBilling, + ); + + if (!consolidatedBillingEnabled) { + return true; + } + + const organization = await organizationService.get(route.params.organizationId); + + if (!organization.hasProvider) { + return true; + } + + const provider = await providerService.get(organization.providerId); + + if (!provider) { + return true; + } + + return provider.providerStatus !== ProviderStatusType.Billable; +}; diff --git a/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts b/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts index 8ca7226b97..4af0662875 100644 --- a/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts +++ b/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts @@ -5,6 +5,7 @@ import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstraction import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { OrganizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard"; +import { organizationIsUnmanaged } from "../../billing/guards/organization-is-unmanaged.guard"; import { WebPlatformUtilsService } from "../../core/web-platform-utils.service"; import { PaymentMethodComponent } from "../shared"; @@ -29,7 +30,7 @@ const routes: Routes = [ { path: "payment-method", component: PaymentMethodComponent, - canActivate: [OrganizationPermissionsGuard], + canActivate: [OrganizationPermissionsGuard, organizationIsUnmanaged], data: { titleId: "paymentMethod", organizationPermissions: (org: Organization) => org.canEditPaymentMethods, @@ -38,7 +39,7 @@ const routes: Routes = [ { path: "history", component: OrgBillingHistoryViewComponent, - canActivate: [OrganizationPermissionsGuard], + canActivate: [OrganizationPermissionsGuard, organizationIsUnmanaged], data: { titleId: "billingHistory", organizationPermissions: (org: Organization) => org.canViewBillingHistory,